はじめに
FRRouting(https://frrouting.org/)はオープンソースのルーティングプロトコルソフトウェアで、SONiCの中にも組み込まれるなど幅広く活用されています。FRRoutingはソースコードが公開されていますので、自分でビルドして使用する、あるいはコンテナイメージを作成してデプロイすることも容易です。ただ、配布されているFRRoutingのDockerイメージですと、IPv6の通信の試験をする際に様々な問題が発生しました。そこで、IPv4/IPv6の両方の通信確認が可能なdocker composeを作成しましたので、この記事にてサンプルを共有したいと思います。このページでは、以下のネットワークをdocker composeにて実現してみます。


docker compose 含めたファイルの準備
今回の実験は、以下のホスト環境にて実施しています。
- Ubuntu 20.04
- Docker version 27.3.1
- Docker Compose version v2.29.7
frr_docker +- docker-compose.yml +- config/ | +- frr1/ | | +- daemons | | +- frr.conf | | +- vtysh.conf | | | +- frr2/ | +- daemons | +- frr.conf | +- vtysh.conf | +- frr/用意した docker-compose.yml は以下です。いくつか細工をした点がありますので、そちらは後述します。
networks: host1_net: driver: macvlan enable_ipv6: true ipam: config: - subnet: 10.0.1.0/24 gateway: 10.0.1.2 - subnet: 2001:db8:0:1::/64 gateway: 2001:db8:0:1::2 host2_net: driver: macvlan enable_ipv6: true ipam: config: - subnet: 10.0.2.0/24 gateway: 10.0.2.2 - subnet: 2001:db8:0:2::/64 gateway: 2001:db8:0:2::2 frr_net: driver: macvlan enable_ipv6: true ipam: config: - subnet: 10.0.3.0/24 gateway: 10.0.3.3 - subnet: 2001:db8:0:3::/64 gateway: 2001:db8:0:3::3 services: host1: container_name: host1 image: alpine cap_add: - CAP_NET_RAW - NET_ADMIN command: /bin/sh -c "ip route add default via 10.0.1.1 && ip route add default via 2001:db8:0:1::1 && tail -f /dev/null" networks: host1_net: ipv4_address: 10.0.1.11 ipv6_address: 2001:db8:0:1::11 host2: container_name: host2 image: alpine cap_add: - CAP_NET_RAW - NET_ADMIN command: /bin/sh -c "ip route add default via 10.0.2.1 && ip route add default via 2001:db8:0:2::1 && tail -f /dev/null" networks: host2_net: ipv4_address: 10.0.2.11 ipv6_address: 2001:db8:0:2::11 frr1: container_name: frr1 image: local/frr cap_add: - CAP_NET_RAW - NET_ADMIN - SYS_ADMIN build: context: ./frr/docker/debian/ dockerfile: Dockerfile volumes: - ./config/frr1/daemons:/etc/frr/daemons - ./config/frr1/frr.conf:/etc/frr/frr.conf - ./config/frr1/vtysh.conf:/etc/frr/vtysh.conf sysctls: - net.ipv6.conf.all.disable_ipv6=0 - net.ipv6.conf.all.forwarding=1 networks: frr_net: ipv4_address: 10.0.3.1 ipv6_address: 2001:db8:0:3::1 host1_net: ipv4_address: 10.0.1.1 ipv6_address: 2001:db8:0:1::1 frr2: container_name: frr2 image: local/frr cap_add: - CAP_NET_RAW - NET_ADMIN - SYS_ADMIN build: context: ./frr/docker/debian/ dockerfile: Dockerfile volumes: - ./config/frr2/daemons:/etc/frr/daemons - ./config/frr2/frr.conf:/etc/frr/frr.conf - ./config/frr2/vtysh.conf:/etc/frr/vtysh.conf sysctls: - net.ipv6.conf.all.disable_ipv6=0 - net.ipv6.conf.all.forwarding=1 networks: frr_net: ipv4_address: 10.0.3.2 ipv6_address: 2001:db8:0:3::2 host2_net: ipv4_address: 10.0.2.1 ipv6_address: 2001:db8:0:2::1"config/frr1/frr.conf"は、frr1コンテナのFRRoutingの設定で、内容は以下の通りです。
frr defaults traditional hostname frr1 service integrated-vtysh-config ! interface lo ip address 1.1.1.1/32 exit ! router bgp 65001 bgp router-id 1.1.1.1 no bgp ebgp-requires-policy no bgp default ipv4-unicast bgp bestpath as-path multipath-relax bgp bestpath compare-routerid bgp bestpath peer-type multipath-relax neighbor 10.0.3.2 remote-as 65002 neighbor 2001:db8:0:3::2 remote-as 65002 ! address-family ipv4 unicast redistribute connected neighbor 10.0.3.2 activate exit-address-family ! address-family ipv6 unicast redistribute connected neighbor 2001:db8:0:3::2 activate exit-address-family exit !同様に、"config/frr2/frr.conf"は、frr2コンテナのFRRoutingの設定で、内容は以下の通りです。
frr defaults traditional hostname frr2 service integrated-vtysh-config ! interface lo ip address 2.2.2.2/32 exit ! router bgp 65002 bgp router-id 2.2.2.2 no bgp ebgp-requires-policy no bgp default ipv4-unicast bgp bestpath as-path multipath-relax bgp bestpath compare-routerid bgp bestpath peer-type multipath-relax neighbor 10.0.3.1 remote-as 65001 neighbor 2001:db8:0:3::1 remote-as 65001 ! address-family ipv4 unicast redistribute connected neighbor 10.0.3.1 activate exit-address-family ! address-family ipv6 unicast redistribute connected neighbor 2001:db8:0:3::1 activate exit-address-family exit !また、FRRoutingの設定のvtysh.confは以下で、
service integrated-vtysh-configdaemonsは以下です。
bgpd=yes ospfd=no ospf6d=no ripd=no ripngd=no isisd=no pimd=no ldpd=no nhrpd=no eigrpd=no babeld=no sharpd=no pbrd=no bfdd=yes fabricd=no vrrpd=no pathd=no vtysh_enable=yes zebra_options=" -A 127.0.0.1 -s 90000000" bgpd_options=" -A 127.0.0.1" ospfd_options=" -A 127.0.0.1" ospf6d_options=" -A ::1" ripd_options=" -A 127.0.0.1" ripngd_options=" -A ::1" isisd_options=" -A 127.0.0.1" pimd_options=" -A 127.0.0.1" ldpd_options=" -A 127.0.0.1" nhrpd_options=" -A 127.0.0.1" eigrpd_options=" -A 127.0.0.1" babeld_options=" -A 127.0.0.1" sharpd_options=" -A 127.0.0.1" pbrd_options=" -A 127.0.0.1" staticd_options="-A 127.0.0.1" bfdd_options=" -A 127.0.0.1" fabricd_options="-A 127.0.0.1" vrrpd_options=" -A 127.0.0.1" pathd_options=" -A 127.0.0.1"このdokcer composeでは、FRRoutingのソースコードから、dockerイメージを作成して使用する内容にしています。ですので、トップディレクトリのfrr_dockerにて以下のコマンドを実施して、FRRoutingのソースコードを配置します(ここでは、FRRoutingのバージョンは10.2.1を指定しました)。
git clone https://github.com/FRRouting/frr.git cd frr git checkout frr-10.2.1以上で、必要なファイルの準備は完了です。
docker compose の実行と動作確認
トップディレクトリのfrr_dockerにて、docker composeの以下のコマンドを実行して、実験環境を起動します。
docker compose up -d初回のdocker composeの実行時は、FRRoutingのソースコードからDockerイメージのビルドを実行するため、少し時間がかかります。Dockerイメージのビルドが完了しましたら、作成したイメージを使って各コンテナが作成されます。Dockerコンテナの起動状態を確認すると、以下のようにfrr1、frr2、host1、host2のコンテナが起動しています。
~/frr_docker$ docker compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS frr1 local/frr "/usr/bin/tini -- /u…" frr1 About a minute ago Up About a minute frr2 local/frr "/usr/bin/tini -- /u…" frr2 About a minute ago Up About a minute host1 alpine "/bin/sh -c 'ip rout…" host1 About a minute ago Up About a minute host2 alpine "/bin/sh -c 'ip rout…" host2 About a minute ago Up About a minuteまず、frr1コンテナにてBGPのネイバー接続の状態を確認すると、以下の通り、IPv4とIPv6の両方について、BGPのセッションが確立されていることが確認できます。
~/frr_docker$ docker exec frr1 vtysh -c "show bgp summary" IPv4 Unicast Summary: BGP router identifier 1.1.1.1, local AS number 65001 VRF default vrf-id 0 BGP table version 5 RIB entries 9, using 1152 bytes of memory Peers 1, using 24 KiB of memory Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc 10.0.3.2 4 65002 9 9 5 0 0 00:02:24 3 5 N/A Total number of neighbors 1 IPv6 Unicast Summary: BGP router identifier 1.1.1.1, local AS number 65001 VRF default vrf-id 0 BGP table version 3 RIB entries 5, using 640 bytes of memory Peers 1, using 24 KiB of memory Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc 2001:db8:0:3::2 4 65002 8 9 3 0 0 00:02:22 2 3 N/A Total number of neighbors 1次に、IPv4とIPv6のルーティング情報を確認すると、以下の通り、BGPにてルーティング情報が交換されていることが分かります。
~/frr_docker$ docker exec frr1 vtysh -c "show ip route" Codes: K - kernel route, C - connected, L - local, S - static, R - RIP, O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP, T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR, f - OpenFabric, t - Table-Direct, > - selected route, * - FIB route, q - queued, r - rejected, b - backup t - trapped, o - offload failure L * 1.1.1.1/32 is directly connected, lo, weight 1, 00:03:49 C>* 1.1.1.1/32 is directly connected, lo, weight 1, 00:03:49 B>* 2.2.2.2/32 [20/0] via 10.0.3.2, eth0, weight 1, 00:03:47 C>* 10.0.1.0/24 is directly connected, eth1, weight 1, 00:03:49 L>* 10.0.1.1/32 is directly connected, eth1, weight 1, 00:03:49 B>* 10.0.2.0/24 [20/0] via 10.0.3.2, eth0, weight 1, 00:03:47 C>* 10.0.3.0/24 is directly connected, eth0, weight 1, 00:03:49 L>* 10.0.3.1/32 is directly connected, eth0, weight 1, 00:03:49 ~/frr_docker$ docker exec frr1 vtysh -c "show ipv6 route" Codes: K - kernel route, C - connected, L - local, S - static, R - RIPng, O - OSPFv3, I - IS-IS, B - BGP, N - NHRP, T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR, f - OpenFabric, t - Table-Direct, > - selected route, * - FIB route, q - queued, r - rejected, b - backup t - trapped, o - offload failure C>* 2001:db8:0:1::/64 is directly connected, eth1, weight 1, 00:04:01 L>* 2001:db8:0:1::1/128 is directly connected, eth1, weight 1, 00:04:01 B>* 2001:db8:0:2::/64 [20/0] via fe80::42:aff:fe00:302, eth0, weight 1, 00:03:57 C>* 2001:db8:0:3::/64 is directly connected, eth0, weight 1, 00:04:01 L>* 2001:db8:0:3::1/128 is directly connected, eth0, weight 1, 00:04:01 C * fe80::/64 is directly connected, eth1, weight 1, 00:03:59 C>* fe80::/64 is directly connected, eth0, weight 1, 00:03:59試しにhost1からhost2にIPv4とIPv6の両方でpingを実行すると、以下のように疎通を確認できます。
~/frr_docker$ docker exec host1 ping 10.0.2.11 PING 10.0.2.11 (10.0.2.11): 56 data bytes 64 bytes from 10.0.2.11: seq=0 ttl=62 time=0.167 ms 64 bytes from 10.0.2.11: seq=1 ttl=62 time=0.134 ms ~/frr_docker$ docker exec host1 ping 2001:db8:0:2::11 PING 2001:db8:0:2::11 (2001:db8:0:2::11): 56 data bytes 64 bytes from 2001:db8:0:2::11: seq=0 ttl=62 time=0.237 ms 64 bytes from 2001:db8:0:2::11: seq=1 ttl=62 time=0.152 ms同様にhost2からhost1も以下のようにpingが成功します。
~/frr_docker$ docker exec host2 ping 10.0.1.11 PING 10.0.1.11 (10.0.1.11): 56 data bytes 64 bytes from 10.0.1.11: seq=0 ttl=62 time=0.111 ms 64 bytes from 10.0.1.11: seq=1 ttl=62 time=0.132 ms ~/frr_docker$ docker exec host2 ping 2001:db8:0:1::11 PING 2001:db8:0:1::11 (2001:db8:0:1::11): 56 data bytes 64 bytes from 2001:db8:0:1::11: seq=0 ttl=62 time=0.096 ms 64 bytes from 2001:db8:0:1::11: seq=1 ttl=62 time=0.142 ms
docker composeの工夫した点
docker composeを使って、FRRoutingの実験環境を作成する際に、いくつか工夫した点がありますので、ここでいくつか紹介いたします。
1) docker networkのデフォルトゲートウェイ
docker composeにてnetworkを作成し、そのIPセグメントを設定すると、dockerにて自動的にデフォルトゲートウェイのIPアドレスが設定されます。ただ、実際にFRRoutingやホストの通信に使いたいデフォルトゲートウェイのIPアドレスを重複することもあるため、docker composeの以下のように、実験で使用しないIPアドレスをdocker networkのデフォルトゲートウェイに設定しました。具体的には、以下の箇所で設定しているゲートウェイアドレスの10.0.1.2と2001:db8:0:1::2は、実験に使用しないダミーのアドレスです。networks: host1_net: driver: macvlan enable_ipv6: true ipam: config: - subnet: 10.0.1.0/24 gateway: 10.0.1.2 - subnet: 2001:db8:0:1::/64 gateway: 2001:db8:0:1::2hostコンテナには、上記とは別にデフォルトゲートウェイアドレスを明示的に設定する必要があります。そのため、host1とhost2のコンテナには以下のようにデフォルトゲートウェイを設定するip routeコマンドを追加しています(以下はhost1の例)。
command: /bin/sh -c "ip route add default via 10.0.1.1 && ip route add default via 2001:db8:0:1::1 && tail -f /dev/null"
2) FRRoutingのDockerイメージのビルド
FRRoutingの配布されているDockerイメージはalpineをベースにしているのですが、IPv6の通信が正常に動作しないケースがありました。ですので、今回はFRRoutingのソースコードを用意して、local/frrのタグがついたDebianベースのDockerイメージを作成しています。具体的には、docker composeでは以下の通り記載しています。image: local/frr ... build: context: ./frr/docker/debian/ dockerfile: Dockerfile上記の通りに記載することで、docker compose upコマンドを実行時に、frr/docker/debian/にあるDockerfileを使って、local/frrのDockerイメージがビルドされます。
3) FRRoutingコンテナのIPv6中継の有効化
FRRoutingコンテナにてIPv6通信を中継できるようにするために、sysctlにてIPv6の中継を有効にしておく必要があります。そのため、FRRoutingコンテナにて以下のsysctlの定義をdocker composeに記載しています。sysctls: - net.ipv6.conf.all.disable_ipv6=0 - net.ipv6.conf.all.forwarding=1
最後に
以上、docker composeを使って、FRRoutingのIPv4/IPv6通信試験環境の構築方法を説明いたしました。この記事では記載していませんが、docker composeでは、docker networkに物理サーバのNICを割り当てることで、サーバの外の物理装置とも接続する試験環境を用意することが可能です。そうすることで、docker composeで用意したFRRoutingを使って、物理装置に対してL3ルーティングプロトコルの試験を容易に実施することが可能になります。この記事が何かの役に立てれば幸いです。