お腹.ヘッタ。

関数型とかセキュリティとか勉強したい。進捗つらぽよ

ExaBGPにSRv6 MUPを実装をした時のメモ

Why

exaBGPに MUP-BGP を追加しました。パッチを出すのにちょくちょく困ってたのでメモを公開しておきます。

当該パッチはこちら github.com

使い方はこちらgobgpとの相互接続例をこのリンクで示してます。

追記: CML2で遊べるように cloudinitファイルを書いてみました。宜しければどうぞー

設計に関しての勘所

大きくわけて設定のためのパーサーとパケットのパーサーを書くというのが、BGPに機能を実装するということです。

具体的には前者のあたりはsrc/exabgp/configuration/を中心としたコード、後者は src/exabgp/bgp/message/update/を中心としたコードに手を加えることになります。

例えば前者のコードで announce セクションで動作するには 例えば@ParseAnnounce.register('mup', 'extend-name', 'ipv4') といったデコレーターをつけた上でパーサーコードを作り、先ほどの後者で記述されるTypeに合わせた設定を返します。

詳しくはPRにあるコードを見てください。

デバッグとtest方法

これはpythonなのでデバッグには import pdb;pdb.set_trace() を使うのが一番便利です。実質ブレークポイントなのでこれを入れて保存して実行してあげるのが良い感じです。 また、パケットの構造がミスってるかどうかを知りたい場合は受け取る方を行う方がわかりやすいです。理由としては受け取り側はstack traceが出るようになっていて尚且つ Notificationメッセージなどでわかるケースがあるからです。

他にもこのようにログとしてもヒントが出てきたりします。13:22:02 1656739 outgoing-13 >> NOTIFICATION (3,0,“invalid ipv4 mup next-hop length 16 expected 4”)

パースの方だと先ほどのような stack trace が出てこないので気をつける必要があります。

デバッグを通じて完成した場合はテストを書いてあげることもしたくなるはずです。 etc/exabgp/<confname>.conf (合わせてqa/ci/<confname>.ci<confname>.confと書いてあげる必要があります)に設定を書いてあげる必要があります。 これは実装物の設定例になる物です。

詳しくはPRを参照すると良いですが、肝は neighbor 127.0.0.1 にした上でコンフィグを設定し、exabgp -d <confname>.conf-d オプションを追加した上でログからupdateメッセージを取得して qa/ci/<confname>.msgに加筆する必要があります。

以下にgobgpの設定とexabgpの設定、先に示したようなgistに書いたネットワークを変形させたnetnsを示します。

#!/bin/bash

set -eu

if [[ $(id -u) -ne 0 ]] ; then
    echo "Please run with sudo"
    exit 1
fi

run () {
    echo "$@"
    "$@" || exit 1
}

destroy_network () {
    run ip netns del red
    run ip netns del blue
}

stop () {
    destroy_network
}

trap stop 0 1 2 3 13 14 15

# exec functions
run ip link add veth-red-blue type veth peer name veth-blue-red

run ip netns add red
run ip link set veth-red-blue netns red
run ip netns exec red ip link set up lo
run ip netns exec red ip link set up veth-red-blue 
run ip netns exec red ip addr add 10.0.0.1/24 dev veth-red-blue

status=0; $SHELL || status=$?
exit $status

gobgp.toml

[global.config]
    as = 65000
    router-id = "127.0.0.1"

[[neighbors]]
    [neighbors.config]
        peer-as = 65000
        local-as = 65000
        neighbor-address = "127.0.0.1"
    [[neighbors.afi-safis]]
        [neighbors.afi-safis.config]
            afi-safi-name = "ipv4-mup"
    [[neighbors.afi-safis]]
        [neighbors.afi-safis.config]
            afi-safi-name = "ipv6-mup"

exabgp.conf

neighbor 127.0.0.1 {
    router-id 10.0.0.1;
    local-address 127.0.0.1;
    local-as 65000;
    peer-as 65000;

    family {
            ipv4 mup;
            ipv6 mup;
    }
    announce {
        // gistを参考に所望のannouceを記述する
    }
}

これらの設定コンフィグを保存した後は以下のようにして実行します。

# red
sudo ip netns exec blue gobgpd -f ./gobgp.toml

# separate terminal exec
sudo ip netns exec red \
    /home/ubuntu/exabgp/sbin/exabgp \
    -d -v\
    ./exabgp.conf

以下のように update messageがログに流れるはずです。

23:19:14 1720215 rib             insert mup:t1st::100:100:2001:db8:1:1::1/128:12345:9:128:2001::1 extended-community target:10:10                                                    [733/1918]
23:19:14 1720215 rib             insert mup:t2st::100:100:12345:128:2001::1 extended-community target:10:10                                                                                    
23:19:14 1720215 outgoing-1      sending TCP payload ( 128) FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF 0080 0200 0000 6940 0101 0040 0200 4005 0400 0000 64C0 1008 0002 000A 0000 000A C028 2505 0022 0001 001E 0020 010D B800 0100 0100 0000 0000 0000 0000 0048 0001 0006 4018 1000 0000 800E 2500 0155 1020 0100 0000 0000 0000 0000 0000 0000 0100 0100 010C 0000 0064 0000 0064 180A 0001
(中略)
23:19:14 1720215 outgoing-1      >> 8 UPDATE(s) 

そしたらこの outgoing-1 のバイナリ列がupdate messageになっているのでそれを取得して整形します

整形する形は以下のようにします

1:raw:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0080:02:000000694001010040020040050400000064C010080002000A0000000AC028250500220001001E0020010DB800010001000000000000000000004800010006401810000000800E250001551020010000000000000000000000000001000100010C0000006400000064180A0001

肝としては、接頭子にルールナンバーとtype,あとは取得したログの空白行を消して、初めのFの連続までをコロン、0080をコロン、02をコロンにして記述します。 ざっくり中身を説明すると

  • F~F: marker
  • 0080: lenght
  • 02: update message type

あとは以下のコマンドを実行して testが動作するかを見ます。これの作業を加えることで機能のテストが無事完了します。

./qa/bin/functional encoding

追記(2023/3/20)

もっと便利な方法があったのでメモしておく。 (ついでに引っかかりポイントも) まず引っかかりポイントとして何かしらの原因で test processが動いてるゆえにテストがゾンビ化してそちらにテスト実行時に繋がってしまい大変困るケースがある。遠慮なく pkill -f python3 とかで殺しておこう。

で、テストの時にnetnsとかはだるいと思うのでどうするかというとupdate messageを見るのではなくて、テストのunexpect エラーから取ってくると楽。具体的には以下のようなイメージ。この時の B は区別されてる列記としたラベルで以下のように list オプションで確認できる。

./qa/bin/functional encoding --list

./qa/bin/functional encoding --server B

./qa/bin/functional encoding --client B

こんな感じで出てくる。エラー自体のタイミングでクライアントの接続を切断されるので、そこではupdate messageと合わせて突き合わせれば良い。

new session:                                                                                                                                                                  
open recv   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0047:01:04FDE800B40A0000012A02060104000100010206010400010055020641040000FDE8020C400A80B4000101800001558002020600                 
open sent   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0047:01:04FDE800B40A0000022A02060104000100010206010400010055020641040000FDE8020C400A80B4000101800001558002020600                 
msg  recv   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0013:04:                                                                                                                         
msg  recv   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0017:02:00000000                                                                                                                 
msg  recv   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:001E:02:00000007900F0003000155                                                                                                   
msg  recv   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0030:02:00000015400101004002004003040A0001FE400504000000C8180A0001                                                               
msg  recv   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:0080:02:000000694001010040020040050400000064C010080002000A0000000AC028250500220001001E0020010DB8000100010000000000000000000048000
10006401810000000800E250001551020010000000000000000000000000001000100010C0000006400000064180A0001                                                                             
msg  recv   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:001B:02:0004180A00010000                                                                                                         
                                                                                                                                                                              
unexpected message:                                                                                                                                                           
received    FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF:001B:02:0004180A00010000      

終わりに

ドキュメントやコードは見やすいように整備されてて、迅速なレビューが素晴らしいですがciとmake fmtのルールが異なったり長い間使われてるOSSなので多少レガシーであることは事実でコードのメンテナンスは難しいなぁと思いました。

一方初めてBGPの機能実装を行いましたが比較的簡単に実装できるOSSというのは良い作品だと思ったのでぜひ今後も機会があればコントリビュートしていきたいと思いました。