この記事は BBSakura Networksアドベントカレンダー 2022 の2日目の記事です。
1日目の記事は早坂さんの 勝手にレポート!BBSakura の社員はどんな環境で仕事をしているのか? でした。
こんにちは。BBSakura Networksのテックリードを担当している日下部( @higebu )です。
普段はモバイルコアを開発しているチームのリーダーをしたり、社内のエンジニアが働きやすくなるような取り組みをやっています。
本日は最近行った GoBGP に BGP Extensions for the Mobile User Plane (MUP) SAFI を実装した話をしようと思います。
ソフトバンク株式会社様のプレスリリース、MECやネットワークスライシングを低コストかつ容易に実現する「SRv6 MUP」の開発に成功 にエンドースメントを出させていただいて以来、SRv6 MUPについて何も表に出しておりませんでしたが、何かをやっていることがお分かりいただけると思います。
※本記事で参照しているインターネットドラフトは下記の通りです。時間が経つと内容が古くなる可能性がありますので、ご了承ください。 - draft-ietf-dmm-srv6-mobile-uplane-22 - draft-mhkk-dmm-srv6mup-architecture-04 - draft-mpmz-bess-mup-safi-01
また、正しくないことを書いていたらご指摘お願いします 🙇
GoBGP とは
GoBGP とはGo言語で実装されたBGPデーモンで、基本的なアーキテクチャは下記のようになっています。
また、BGPプロトコルを話す部分はライブラリとして公開されており、自身のアプリケーションにBGPプロトコルを扱う機能を足すことができます。
具体的な例として、CiliumでBGPプロトコルのネイティブサポートのためにGoBGPが採用されています。
BGP Extensions for the Mobile User Plane (MUP) SAFI とは
IETFで標準化中の SRv6 Mobile User Plane(MUP) の アーキテクチャ では、BGPによってMUP Segment情報やセッション情報を変換した経路情報を広報することができると書いてあります。
このときに使うBGPプロトコルの拡張が BGP Extensions for the Mobile User Plane (MUP) SAFI です。
BGP-MUP SAFIの番号は8月にIANAでアサインされていて、番号は 85
です。(Subsequent Address Family Identifiers (SAFI) Parameters)
また、SRv6 MUP Extended Communityの番号も8月にIANAでアサイン済みで、 0x0c
となっています。(BGP Transitive Extended Community Types)
これらの番号がアサインされたのを見てGoBGPでの実装を始めました。
後はインターネットドラフトを見てくださいでは後半の話がよくわからないと思いますので、MUP SAFIの構造、4つのRoute TypeとMUP Extended Communityについて簡単に説明します。
BGP MUP SAFI
BGP-MUP SAFIはBGP-MUPのために新しく定義されたSAFI(Subsequent Address Family Identifiers)です。 また、新しくBGP-MUPのためのNLRI、BGP-MUP NLRIも定義されており、構造は下記の通りになっています。
+-----------------------------------+ | Architecture Type (1 octet) | +-----------------------------------+ | Route Type (2 octets) | +-----------------------------------+ | Length (1 octet) | +-----------------------------------+ | Route Type specific (variable) | +-----------------------------------+
Architecture Type
は今のところ 3gpp-5g
のみで、 1
になります。Route Type
は下記の4つのTypeがあり、 Route Type specific (variable)
には Route Type
毎に違う値が入ります。
+ 1 - Interwork Segment Discovery route; + 2 - Direct Segment Discovery route; + 3 - Type 1 Session Transformed (ST) route; + 4 - Type 2 Session Transformed (ST) route;
BGP Interwork Segment Discovery route
Interwork Segment Discovery route(ISD) はInterwork Segment情報を広報するときに使います。Interwork SegmentというのはモバイルネットワークのUプレーンプロトコルとMUP Segmentの間の接続性を提供するMUP Segmentのことです。
構造は下記の通りです。
+-----------------------------------+ | RD (8 octets) | +-----------------------------------+ | Prefix Length (1 octet) | +-----------------------------------+ | Prefix (variable) | +-----------------------------------+
3gpp-5g
ではPrefixはgNodeBのN3インターフェースのPrefixになります。
ドラフトの 3.3.1. Generation of the Interwork Segment Discovery route を見ると、Route Target Extended Communityを付けるのが必須になっており、MP_REACH_NLRIのnexthopアドレスはIPv6でないといけません。
また、Prefix SID Attributeも必須で、AFIがAFI_IPの場合はFunctionを End.M.GTP4.E、 AFIがAFI_IP6の場合はFunctionを End.M.GTP6.E にすることになっています。
BGP Direct Segment Discovery route
Direct Segment Discovery route(DSD)はDirect Segment情報を広報するときに使います。Direct SegmentというのはMUP Segment同士の接続性を提供するMUP Segmentのことです。
構造は下記の通りです。
+-----------------------------------+ | RD (8 octets) | +-----------------------------------+ | Address (4 or 16 octets) | +-----------------------------------+
AddressにはPEのユニークなID(Router IDなど)が入ります。
ドラフトの 3.3.4. Generation of the Direct Segment Discovery route を見ると、Route Target Extended CommunityとMUP Extended Communityが必須になっており、ISD同様、MP_REACH_NLRIのnexthopアドレスはIPv6でないといけません。
MUP Extended CommunityにはDirect Segment Identifierを入れる必要があります。
また、こちらもPrefix SID Attributeが必須で、End.DT4/6、End.DX4/6、End.M.GTP4/6.E辺りが使われそうということになっています。
BGP Type 1 Session Transformed (ST) Route
Type 1 Session Transformed (ST) RouteはMUP-Cが広報する、モバイルネットワーク側のセッション情報を変換した経路情報のうちの1つで、Downlink方向で使う情報が入っており、PEではISDの情報と一緒に使います。
構造は下記の通りです。
+-----------------------------------+ | RD (8 octets) | +-----------------------------------+ | Prefix Length (1 octet) | +-----------------------------------+ | Prefix (variable) | +-----------------------------------+ | Architecture specific (variable) | +-----------------------------------+
3gpp-5g
ではPrefixはUEのアドレスになります。
また、3.1.3.1. 3gpp-5g Specific BGP Type 1 ST Route に 3gpp-5g
のときの Architecture specific (variable)
が書いてあり、構造は下記の通りです。
+-----------------------------------+ | TEID (4 octets) | +-----------------------------------+ | QFI (1 octet) | +-----------------------------------+ | Endpoint Address Length (1 octet) | +-----------------------------------+ | Endpoint Address (variable) | +-----------------------------------+
これはGTPトンネルの情報になっており、Endpoint AddressはgNodeBのN3インターフェースのアドレスになります。SRv6 MUPではこの情報がSRv6パケットのSIDに含まれるArgs.Mob.Sessionに入り、最終的にgNodeB宛てのGTPのパケットのヘッダにエンコーディングされます。
3.3.7. Generation of the Type 1 ST Route を見ると、Route Target Extended Communityを付けるべき(SHOULD)となっており、正しいDirect Segmentにimportされるようにしましょうと書いてあります。
また、nexthopはMUP-Cのアドレスです。
BGP Type 2 Session Transformed (ST) Route
Type 2 Session Transformed (ST) RouteはMUP-Cが広報する、モバイルネットワーク側のセッション情報を変換した経路情報のうちの1つで、Uplink方向で使う情報が入っており、PEではDSDの情報と一緒に使います。
構造は下記の通りです。
+-----------------------------------+ | RD (8 octets) | +-----------------------------------+ | Endpoint Length (1 octet) | +-----------------------------------+ | Endpoint Address (variable) | +-----------------------------------+ | Architecture specific Endpoint | | Identifier (variable) | +-----------------------------------+
Endpoint AddressはUPFのN3インターフェースのアドレスです。
また、3.1.4.1. 3gpp-5g Specific BGP Type 2 ST Route に 3gpp-5g
のときの Architecture specific Endpoint Identifier (variable)
が書いてあり、構造は下記の通りです。
+-----------------------------------+ | TEID (0-4 octets) | +-----------------------------------+
このTEIDはコアサイド(UPF側)のTEIDです。
3.3.10. Generation of the Type 2 ST Route を見ると、Route Target Extended Communityが必須になっていて、正しいInterwork Segmentにimportされるようにしなければならないことになっています。
また、MUP Extended Communityも必須で、この経路情報を使うDirect Segmentに対応したDirect Segment Identifierを付けなければなりません。 nexthopはType 1同様、MUP-Cのアドレスです。
GoBGP で BGP-MUP を使う方法
※ここからGoBGPのリポジトリへのリンクやコードの説明が出てきますが、 2022/12/02時点の最新のコミット をベースにしています。
使い方は GoBGP のリポジトリ内のドキュメント に記載しているのですが、ここでも簡単に説明します。
まず、下記のように ipv4-mup
を有効にしたコンフィグを用意し、gobgpdを起動します。
[global.config] as = 65000 router-id = "10.0.0.1" local-address-list = ["10.0.0.1"] [[neighbors]] [neighbors.config] peer-as = 65000 local-as = 65000 neighbor-address = "10.0.0.2" [[neighbors.afi-safis]] [neighbors.afi-safis.config] afi-safi-name = "ipv4-mup"
こうすると ipv4-mup
の address familiy が有効になるので、下記のようなコマンドでBGP-MUPの経路をRIBに追加できます。
gobgp global rib add -a ipv4-mup isd 10.0.0.0/24 rd 100:100 prefix 2001:db8:1:1::/64 locator-node-length 24 function-length 16 behavior ENDM_GTP4E rt 10:10 nexthop 2001::2 gobgp global rib add -a ipv4-mup dsd 10.0.0.1 rd 100:100 prefix 2001:db8:1:1::/64 locator-node-length 24 function-length 16 behavior END_DT4 rt 10:10 mup 10:10 nexthop 2001::2 gobgp global rib add -a ipv4-mup t1st 192.168.0.1/32 rd 100:100 rt 10:10 teid 12345 qfi 9 endpoint 10.0.0.1 nexthop 10.0.0.2 gobgp global rib add -a ipv4-mup t2st 10.0.0.1 rd 100:100 rt 10:10 teid 12345 mup 10:10 nexthop 10.0.0.2
ipv6-mup
を使いたいときは ipv4-mup
の部分を ipv6-mup
に置き替えてください。
ドキュメントには 2つのnetnsの中でgobgpdを起動し、経路を広報する例 を載せています。パケットも見ることができるため、こちらを試してみるのがおすすめです。
パケットを見たい場合は開発版のWiresharkが必要です。 こちら からダウンロードしてください。
yuyarinさんのパッチ が入っているためBGP-MUPのパケットをきれいに見ることができます。
GoBGP に Extended Community と SAFI を追加するには
ここではGoBGPにBGP-MUPを実装したときに行った下記の点について、それぞれ説明します。 - BGPパケットのパース、生成 - RIBで経路を持ってもらう方法 - APIで経路を扱えるようにする方法 - CLIで経路を操作できるようにする方法 - シナリオテストの追加
BGPパケットのパース、生成
BGPパケットのパース、生成に関するコードは全て pkg/packet/bgp にあります。特に1万行以上ある pkg/packet/bgp/bgp.go に多くのコードが集中しているため、ここを読んでいくと大体わかると思いますが、読むのが大変なので、以下にポイントをまとめておきます。
- SAFIの番号などは pkg/packet/bgp/bgp.go の上方にconstで定義されている
- NLRIは AddrPrefixInterface を満たす必要があり、 PrefixDefault、 IPAddrPrefixDefault などの新しいNLRIを作るときに埋め込んで使うことができる共通処理をまとめたstructがある
- Extended Communityは ExtendedCommunityInterface を満たさなければならず、 func ParseExtended(data []byte) (ExtendedCommunityInterface, error) から追加した Extended Community をパースする関数を呼ぶ必要がある
- pkg/packet/bgp/srbehavior.go はapiパッケージのソースを元に自動生成なので注意が必要(生成方法は後述)
今回は bgp.go
に追記が必要な部分以外のBGP-MUP関連のコードをまとめた mup.go を新しく追加しました。
RIBで経路を持ってもらう方法
RIBは internal/pkg/table パッケージで管理されていて、この中の table.go と path.go を見るとNLRI毎の処理をしている部分が見つかります。
具体的には func (v Vrf) ToGlobalPath(path Path) error 、 func (p Path) ToGlobal(vrf Vrf) *Path 、 func (t Table) deletePathsByVrf(vrf Vrf) []*Path です。
ここにcaseを足すことで新しいNLRIがRIBに入るようになります。
新しいNLRIをRIBに入れるには上記のみで良さそうに見えますが、実際はコンフィグの neighbors.afi-safis.config.afi-safi-name
で新しいAFI/SAFIを指定できるようにしないとテーブルが初期化されず、APIやCLIで経路を投入しても何も起きません。
コンフィグで新しいAFI/SAFIを扱えるようにするには internal/pkg/config/bgp_configs.go の AfiSafiType に新しいAFI/SAFIを追加します。
今回は ipv4-mup
と ipv6-mup
を追加しました。
APIで経路を扱えるようにする方法
protoファイルの編集
GoBGPはAPI定義に Protocol Buffers を使っており、 api ディレクトリにprotoファイルと生成されたGoのソースファイルが置いてあります。
APIを編集したい場合はここのprotoファイルを編集し、コードを生成することになります。
SAFIの番号は enum Safi に、NLRI や Extended Community は api/attribute.proto に定義されています。
コード生成は tools/grpc/genproto.sh でやっており、コマンドは下記のようになります。(これを実行するには事前に protocコマンドのインストール が必要です。)
./tools/grpc/genproto.sh
また、 api/attribute.proto の enum SRv6Behavior を追加したときは go generate ./pkg/packet/bgp
を実行し、 pkg/packet/bgp/srbehavior.go と pkg/packet/bgp/srbehavior_string.go を生成し直しておく必要があります。
structの詰め替え
APIでやり取りするときに使う自動生成されたstructとbgpパッケージ側で使うstructは違うため、相互に詰め替えが必要になります。
この辺りの処理を行う関数は pkg/apiutil パッケージにまとまっており、 func MarshalNLRI(value bgp.AddrPrefixInterface) (*apb.Any, error) と func UnmarshalNLRI(rf bgp.RouteFamily, an *apb.Any) (bgp.AddrPrefixInterface, error) がこれに当たります。Type SwitchでNLRI毎の処理をしているので、新しいNLRIのcaseを追加することで、APIでの経路情報の操作が可能になります。
Extended Communityも同様で、func NewExtendedCommunitiesAttributeFromNative(a bgp.PathAttributeExtendedCommunities) (api.ExtendedCommunitiesAttribute, error) と func unmarshalExComm(a api.ExtendedCommunitiesAttribute) (bgp.PathAttributeExtendedCommunities, error) に新しいExtended Community用の処理を追加する必要があります。
CLIで経路を操作できるようにする方法
今回追加した MUP Extended Communityをパースするための関数 や、 それぞれのRoute Typeの引数をパースするための関数 を読むと雰囲気がわかると思います。
これらの関数が func parsePath(rf bgp.RouteFamily, args string) (*api.Path, error) という関数から呼ばれることで、追加したコマンドがパースされるようになります。
また、ヘルプメッセージは func modPath(resource string, name, modtype string, args string) error という関数内に書くようになっています。
その他に、経路の表示については cmd/gobgp/neighbor.go にshow系の関数がまとまっていて、こちらも少しコードを追加しています。
cmd/gobgp/common.go にある func checkAddressFamily(def api.Family) (api.Family, error) に使いたい address familiy を書いておかないとCLIがエラーになるところには結構ハマりました。。。
シナリオテストの追加
GoBGPではDockerコンテナとしてgobgpdを起動することで、gobgpd同士や他のBGPデーモンとの経路広報のテストをGitHub Actionsで実行するようになっています。
シナリオは test/scenario_test ディレクトリ配下のPythonスクリプトになっており、テストフレームワークとしては nose が使われています。
今回追加したシナリオは mup_test.py です。
setUpClass に書いてある通り、 test/lib/gobgp.py にある GoBGPContainer クラスを使うと簡単にコンテナを作って、ピアの追加をすることができるようになっており、後はテストを書くだけとなっています。便利ですね。
テストの内容は test_02_add_del_mup_route にあり、経路をadd/delしつつ、addしたときに期待した経路が入っているかどうかチェックしています。
テストの実行方法は README に書いてありますが、コンテナをビルドした後にMUPの場合は下記のようなコマンドでテストできます。
PYTHONPATH=./test python3 test/scenario_test/mup_test.py --gobgp-image=gobgp
GoBGP に BGP Extensions for the Mobile User Plane (MUP) SAFI を実装すると何がうれしいのか
今のところ、世の中には他にOSSのBGP-MUP実装がないのですが、BGP-MUPに対応したGoBGPはMUP-CやMUP-PEを実装したいときのBGPの部分に使うことができます。
また、本来MUP-CのNorthbound API(未定義)から投入することになっているセッション情報を元にしたType 1 ST Route、Type 2 ST RouteをAPIやCLIから投入することができ、SRv6 MUPの検証が捗るようになります。
最後に
今回は、GoBGPに BGP Extensions for the Mobile User Plane (MUP) SAFI を実装した話について紹介させていただきました。
BBSakura NetworksではこのようにIETFで標準化中の新しい技術を使った業務を普段から行っており、BGPやSRv6等のネットワーク技術に明るく、3GPPの仕様が読めて、プログラミングもできる仲間を募集しています。
気になった方は 採用情報 からご連絡ください。
よろしくお願いいたします。