FRRとgrpcでお話しするまでの道のりの記録です。

FRRの準備

土日にFRRoutingでgrpcを試してみました。環境はUbuntu 20.04です。

まず最初に最新のリリースパッケージを試してみましたが、どうやらコンパイルオプション的にgrpcは有効じゃなさそうでした。というわけで最新ソースコードからコンパイル。

事前に必要なパッケージは https://docs.frrouting.org/projects/dev-guide/en/latest/building-frr-for-ubuntu2004.html#installing-dependencies を参考に入れます。 加えて以下を入れます。但しhistoryからたどっていったので正確かどうかわかりません。

sudo apt install libprotobuf-c-dev
sudo apt install protobuf-c-compiler
sudo apt install libgrpc-dev
sudo apt install libgrpc++-dev
sudo apt install libprotoc-dev
sudo apt install protobuf-compiler-grpc

あと、libyang2, libyang2-devが必要です。で、これ最初にfrrの最新リリースのバイナリパッケージを入れた関係で依存関係で入っています。 frrの最新リリースのバイナリパッケージを入れる場合は、 https://deb.frrouting.org/ を見て入れてください。

FRRoutingのコンパイルオプションは以下で実施。

configure --enable-grpc

普通にコンパイルとインストール。

make 
make install

野良パッケージをなるべく入れたくないし、入れる場合は管理したいので、管理のためにporgを使いました。

sudo apt install porg
sudo porg -lD make install

なお、ちなみに、FRRは適当にmasterで作業してしまっています。

Merge: c99978f7b 74dd0c84d
Author: Russ White <russ@riw.us>
Date:   Wed Mar 29 11:05:30 2023 -0400

    Merge pull request #12645 from gpnaveen/ospf_error_msg_enhancements

    tests: [topojson] Update assert/error messages for ospf scripts

Frr起動

本当は/etc配下とかにある(ソースコンパイルなら/usr/local以下)daemonsにファイルに、

zebra_options="  -A 127.0.0.1 -s 90000000  -M grpc:50051"

のように-M grpc:grpcポート番号を追記すべきなんだと思いますが、とりあえず、以下のような感じで、-M grpc:50051を入れて、直接zebra起動

/usr/local/sbin/zebra -d -A localhost -P 2601 -f zebra.conf -i /var/run/frr/zebra.pid -M grpc:50051 --vty_socket /var/run/frr/vty -z /var/run/frr/zserv.api.daemon

grpc CLI clientの準備

grpc標準のgrpc_cli一択だろうと思ったのですが、コンパイルしている間に調べていたら、複数のクライアントの比較で、Evans が良いとの話があり乗り換えました。メルカリの人が作っているんですね。作者による説明もあって使い始めるのに苦労しませんでした。あとGoで書かれていて、release packageをダウンロードして展開して/usr/local/binとか任意のパスに置くだけなので非常に簡単でした。grpc_cliをコンパイルするためにcmakeの環境作った自分に反省です。

evansの実行によるfrrとのgrpc通信

FRRのNorthbound gRPCのDeveloper Guideを読むと、C++も、Pythonも、Rubyも、/frr-interface:libにアクセスしてますね。ということでevansでアクセスしてみます。

何のオペレーションが受け付けれるようになっているのか確認してみます。

evans --proto frr-northbound.proto cli desc

以下がかえって来ました。

frr.Northbound:
service Northbound {
  rpc Commit ( .frr.CommitRequest ) returns ( .frr.CommitResponse );
  rpc CreateCandidate ( .frr.CreateCandidateRequest ) returns ( .frr.CreateCandidateResponse );
  rpc DeleteCandidate ( .frr.DeleteCandidateRequest ) returns ( .frr.DeleteCandidateResponse );
  rpc EditCandidate ( .frr.EditCandidateRequest ) returns ( .frr.EditCandidateResponse );
  rpc Execute ( .frr.ExecuteRequest ) returns ( .frr.ExecuteResponse );
  rpc Get ( .frr.GetRequest ) returns ( stream .frr.GetResponse );
  rpc GetCapabilities ( .frr.GetCapabilitiesRequest ) returns ( .frr.GetCapabilitiesResponse );
  rpc GetTransaction ( .frr.GetTransactionRequest ) returns ( .frr.GetTransactionResponse );
  rpc ListTransactions ( .frr.ListTransactionsRequest ) returns ( stream .frr.ListTransactionsResponse );
  rpc LoadToCandidate ( .frr.LoadToCandidateRequest ) returns ( .frr.LoadToCandidateResponse );
  rpc LockConfig ( .frr.LockConfigRequest ) returns ( .frr.LockConfigResponse );
  rpc UnlockConfig ( .frr.UnlockConfigRequest ) returns ( .frr.UnlockConfigResponse );
  rpc UpdateCandidate ( .frr.UpdateCandidateRequest ) returns ( .frr.UpdateCandidateResponse );
}

設定とかを得るためには、frr.Northbound.Getのrpcを呼び出せばよいのかなと推測できます。 https://github.com/FRRouting/frr/blob/master/grpc/frr-northbound.proto#L111 を見てみるととりあえずpathだけ指定すれば、動きそうだなと推察しましたのでpath/frr-interface:libを指定してみます。

echo '{ "path" : "/frr-interface:lib"}' | evans --proto frr-northbound.proto cli call frr.Northbound.Get

出力結果が改行文字がそのまま出力されて読みにくいです。

{
  "data": {
    "data": "{\n  \"frr-interface:lib\": {\n    \"interface\": [\n      {\n        \"name\": \"c0s0-c0l0\",\n        \"vrf\": \"default\",\n        \"state\": {\n          \"if-index\": 11,\n          \"mtu\": 1500,\n          \"mtu6\": 1500,\n          \"speed\": 10000,\n          \"metric\": 0,\n          \"phy-address\": \"62:21:a2:b8:22:b2\"\n        },\n        \"frr-zebra:zebra\": {\n          \"state\": {\n            \"up-count\": 1,\n            \"down-count\": 0\n          }\n        }\n      },\n      {\n        \"name\": \"lo\",\n        \"vrf\": \"default\",\n        \"state\": {\n          \"if-index\": 1,\n          \"mtu\": 0,\n          \"mtu6\": 65536,\n          \"speed\": 0,\n          \"metric\": 0,\n          \"phy-address\": \"00:00:00:00:00:00\"\n        },\n        \"frr-zebra:zebra\": {\n          \"state\": {\n            \"up-count\": 2,\n            \"down-count\": 0\n          }\n        }\n      }\n    ]\n  }\n}\n"
  },
  "timestamp": "1680917019"
}

ローテクですが、sedで改行文字を改行させてみる。

echo '{ "path" : "/frr-interface:lib"}' | evans --proto frr-northbound.proto cli call frr.Northbound.Get | sed  -e "s/\\\n/\n/g"

出力結果が読みやすくなりましたね。

{
  "data": {
    "data": "{
  \"frr-interface:lib\": {
    \"interface\": [
      {
        \"name\": \"c0s0-c0l0\",
        \"vrf\": \"default\",
        \"state\": {
          \"if-index\": 11,
          \"mtu\": 1500,
          \"mtu6\": 1500,
          \"speed\": 10000,
          \"metric\": 0,
          \"phy-address\": \"62:21:a2:b8:22:b2\"
        },
        \"frr-zebra:zebra\": {
          \"state\": {
            \"up-count\": 1,
            \"down-count\": 0
          }
        }
      },
      {
        \"name\": \"lo\",
        \"vrf\": \"default\",
        \"state\": {
          \"if-index\": 1,
          \"mtu\": 0,
          \"mtu6\": 65536,
          \"speed\": 0,
          \"metric\": 0,
          \"phy-address\": \"00:00:00:00:00:00\"
        },
        \"frr-zebra:zebra\": {
          \"state\": {
            \"up-count\": 2,
            \"down-count\": 0
          }
        }
      }
    ]
  }
}
"
  },
  "timestamp": "1680917125"
}

基本的な動作がわかりました。 それではBGPの情報をとってみましょう。と思ったらこんなDiscussionを発見してしまいました。 動かないらしいです。