SONiC と FRR -- sonic-bgpcfgd 編

## はじめに
前回 [EC SONiC VS で EVPN BGP L2VNI -- ZTP 自動化編](https://qiita.com/cyo-chang/items/3b667aafa32c2009adec) について説明した際、SONiC の config_db.json とfrr.conf の両方を TFTP サーバーに保存する必要がありました。なぜかというと、EC SONiC のデフォルトの routing mode (`docker_routing_config_mode`)  が `split` に設定されているからです。

ネットで調べたところ、なんと split mode は Community のメンバーによって厳しく批判されているそうです。自動デプロイの規模が大きいほど、FRR と SONiC の設定を分けることで管理が難しくなるというわけです。

以上の課題は、SONiC を導入すると誰もが必ず遭遇する問題だと思います。この記事では、Community のルーティングモードを比較し、現状について整理してみます。

:::note warn
SONiC は、sonic-frr-mgmt-framework と sonic-bgpcfgd で FRR config を制御します。本記事は、sonic-bgpcfgd の観点から整理してます。
:::


## routing mode について

現時点 Community SONiC (202311~) のデフォルトは `unified` になっています。つまり、FRRコンフィグは SONiC の Config_db.json から生成されます。

sonic-bgpcfgd による FRR の初期設定フローはこんな感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3307184/f7d9a8fa-e3ee-927e-61fa-fe4e1ccbecba.png)
 
https://github.com/sonic-net/sonic-buildimage/blob/master/src/sonic-yang-models/yang-models/sonic-device_metadata.yang

SONiC YANGの定義から それぞれの routing mode を確認できます。
``` sonic-buildimage/src/sonic-yang-models/yang-models/sonic-device_metadata.yang
leaf docker_routing_config_mode {
    description "This leaf allows different configuration modes for FRR:
                - separated: FRR config generated from ConfigDB, each FRR daemon has its own config file
                - unified: FRR config generated from ConfigDB, single FRR config file
                - split: FRR config not generated from ConfigDB, each FRR daemon has its own config file
                - split-unified: FRR config not generated from ConfigDB, single FRR config file";
    type string {
        pattern "separated|unified|split|split-unified";
    }
    default "unified";
}
```

テーブルでまとめてみると、
| モード      | 説明                                                                                   | frr configuration                                            |
|------------|--------------------------------------------------------------------------------------|---------------------------------------------------------|
| separated  | FRR の設定が ConfigDB から生成され、各 FRR deamon に対して個別の設定ファイルが存在する。                              | 各 deamon 用に個別の設定ファイルを生成する。                              |
| unified    | FRR の設定が ConfigDB から生成され、単一のFRR設定ファイルを使用します。                                         | 単一の設定ファイル `frr.conf` を生成し、その他のFRR設定ファイルを削除する。          |
| split      | FRR の設定は ConfigDB から生成されず、各 FRR deamon に対して個別の設定ファイルが存在する。                             | 各 deamon 用に個別の設定ファイルを生成するが、`frr.conf` は使用しない。              |
| split-unified | FRR の設定は ConfigDB から生成されず、単一の FRR 設定ファイルを使用する。                                        | 単一の設定ファイル `frr.conf` を生成し、その他の FRR 設定ファイルを削除する。          |

実際に sonic-buildimage ソースコードをのぞいてみると、こんな感じです。
``` shell:sonic-buildimage/dockers/docker-fpm-frr/docker_init.sh
if [ -z "$CONFIG_TYPE" ] || [ "$CONFIG_TYPE" == "separated" ]; then
    CFGGEN_PARAMS=" \
        -d \
        -y /etc/sonic/constants.yml \
        -t /usr/share/sonic/templates/bgpd/gen_bgpd.conf.j2,/etc/frr/bgpd.conf \
        -t /usr/share/sonic/templates/zebra/zebra.conf.j2,/etc/frr/zebra.conf \
        -t /usr/share/sonic/templates/staticd/gen_staticd.conf.j2,/etc/frr/staticd.conf \
    "
    MGMT_FRAMEWORK_CONFIG=$(echo $FRR_VARS | jq -r '.frr_mgmt_framework_config')
    if [ -n "$MGMT_FRAMEWORK_CONFIG" ] && [ "$MGMT_FRAMEWORK_CONFIG" != "false" ]; then
        CFGGEN_PARAMS="$CFGGEN_PARAMS \
            -t /usr/local/sonic/frrcfgd/bfdd.conf.j2,/etc/frr/bfdd.conf \
            -t /usr/local/sonic/frrcfgd/ospfd.conf.j2,/etc/frr/ospfd.conf \
        "
    else
        rm -f /etc/frr/bfdd.conf /etc/frr/ospfd.conf
    fi
    sonic-cfggen $CFGGEN_PARAMS
    echo "no service integrated-vtysh-config" > /etc/frr/vtysh.conf
    rm -f /etc/frr/frr.conf
elif [ "$CONFIG_TYPE" == "split" ]; then
    echo "no service integrated-vtysh-config" > /etc/frr/vtysh.conf
    rm -f /etc/frr/frr.conf
    write_default_zebra_config /etc/frr/zebra.conf
elif [ "$CONFIG_TYPE" == "split-unified" ]; then
    echo "service integrated-vtysh-config" > /etc/frr/vtysh.conf
    rm -f /etc/frr/bgpd.conf /etc/frr/zebra.conf /etc/frr/staticd.conf
    write_default_zebra_config /etc/frr/frr.conf
elif [ "$CONFIG_TYPE" == "unified" ]; then
    CFGGEN_PARAMS=" \
        -d \
        -y /etc/sonic/constants.yml \
        -t /usr/share/sonic/templates/gen_frr.conf.j2,/etc/frr/frr.conf \
    "
    sonic-cfggen $CFGGEN_PARAMS
    echo "service integrated-vtysh-config" > /etc/frr/vtysh.conf
    rm -f /etc/frr/bgpd.conf /etc/frr/zebra.conf /etc/frr/staticd.conf \
       
```

## どんなモードを使うべきか

Community の目線から見ると、Routing mode は unified にすべきだと思います。以下の jinja2 テンプレートファイルを参考し、今参照される可能の ConfigDB の Key とその対応の FRR CLI は以下だと整理してみました。

- https://github.com/sonic-net/sonic-buildimage/blob/master/dockers/docker-fpm-frr/frr/bgpd/bgpd.conf.j2
- https://github.com/sonic-net/sonic-buildimage/blob/master/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2
- https://github.com/sonic-net/sonic-buildimage/blob/master/dockers/docker-fpm-frr/frr/bgpd/bgpd.spine_chassis_frontend_router.conf.j2

| Feature Category       | Supported FRR features                                         |
|------------------------|---------------------------------------------------------------|
| **Prefix Lists**       | `ip prefix-list PL_LoopbackV4 permit`<br>`ipv6 prefix-list PL_LoopbackV6 permit`<br>`ip prefix-list LOCAL_VLAN_IPV4_PREFIX permit`<br>`ipv6 prefix-list LOCAL_VLAN_IPV6_PREFIX permit`<br>`ip prefix-list V4_P2P_IP permit`<br>`ipv6 prefix-list V6_P2P_IP permit` |
| **Route Maps**         | `route-map V4_CONNECTED_ROUTES permit`<br>`route-map V6_CONNECTED_ROUTES permit`<br>`route-map HIDE_INTERNAL permit`<br>`set community no-export`<br>`set community additive` |
| **BGP Configuration**  | `router bgp`<br>`bgp log-neighbor-changes`<br>`bgp suppress-fib-pending`<br>`no bgp default ipv4-unicast`<br>`no bgp ebgp-requires-policy`<br>`bgp bestpath as-path multipath-relax`<br>`bgp graceful-restart`<br>`bgp router-id`<br>`network`<br>`redistribute connected`<br>`maximum-paths` |
| **VNET BGP Instance**  | `router bgp vrf`<br>`no bgp default ipv4-unicast`<br>`bgp log-neighbor-changes`<br>`bgp bestpath as-path multipath-relax`<br>`bgp graceful-restart restart-time`<br>`bgp graceful-restart`<br>`bgp router-id`<br>`neighbor remote-as`<br>`neighbor description`<br>`neighbor timers`<br>`neighbor shutdown`<br>`address-family ipv4 unicast`<br>`neighbor activate`<br>`neighbor soft-reconfiguration inbound`<br>`maximum-paths`<br>`address-family l2vpn evpn`<br>`advertise ipv4 unicast` |


ざっと見ると、よく使われる FRR 機能はすでに script 上で統合されていると見えます。
split mode を使う理由はなくなるじゃないかと思う一方、いくつかの欠点も気づきました。

- Native SONiC CLI は以上すべての FRR 設定を提供せず、やっぱり FRR VTYSH CLIに依存してしまいます。
- いくつかの parameters は固定です。例えば、bgp bestpath は as-path multipath-relax 固定です。
- jinja2 テンプレートファイルを確認しながら、直接 config_db にイジせざる得ないです。
- sonic-bgpcfgd は、BGP のみ対応します。

以上の問題がある限り、unified mode がつかいやすいかとも言えないため、現時点は split mode をよく使われているようです。

## 最後に
現状で split mode が unified mode より使いやすいという評価があることは感じます。しかし、長期的な管理や自動化を考えた場合、split modeをやめてunified modeへの移行が理想的かもしれません。確かに、frr.conf と config_db.json を別々管理するのは面倒です。

時間があれば、frr-mgmt-framework について詳しく解説したいと思います。このフレームワークは、sonic-bgpcfgd と同様に config_db.json ファイルを通じて設定を管理できるほか、OSPF や ISIS など他のプロトコルもサポートしています。これにより、frr.conf と config_db.json を別々に管理するのではなく、真の unified mode を実現することが可能です。コミュニティが sonic-bgpcfgd から frr-mgmt-framework への移行を検討することは、今後の大きな注目ポイントとなるでしょう。

## 参考
[SONiC, FRR split configuration, a step backwards?](https://medium.com/sonic-nos/sonic-frr-split-configuration-a-step-backwards-fb4db0e181a1)
 [SONiC で設定する FRRouting](https://speakerdeck.com/imasaruoki/sonicdeshe-ding-surufrrouting)
[Apresia Tech blog: SONiC YANGから設定可能なパラメータの確認方法](https://www.apresiatac.jp/blog/202110115180/)