BGP Graceful Restartに関わる各OSSルーティングプラットフォームの動向調査

はじめに

こんにちは.2021年の4月からITSC Verda室のネットワーク開発チームでアルバイトをしている菅原大和(@drumato)と申します.本記事は,私達のチーム内で調査が必要になった,BGPでNSF(Non-Stop Forwarding)を実現する技術の一つであるBGP Graceful Restartの一般的な話題について整理し発表するものです.また,各OSSルーティングプラットフォームの状況について整理します.付随する話題として,私達LINEがネットワークメンテナンスに力を入れる理由や,ネットワークプロトコルの技術を積極的にキャッチアップする背景についても触れます.Long-Lived Graceful Restart(LLGR)については本記事の範囲外としますのでご了承ください.

disclaimer : 本記事の内容は,執筆者の解釈を多分に含んでおり,RFCの内容を正確に説明するものではありません.本記事の内容について,その正確性は一切保証しません.

背景

LINEのデータセンターネットワーク

私達LINEでは,管理のしやすさや高いスケーラビリティの実現という観点から,Full L3のIP Fabricを構成して運用しています.具体的には,CLOSネットワークの最上段であるExternal Layerから,サーバを収容する各ハイパーバイザ,そしてその上で動作するサーバまで,すべてL3でルーティングされるようになっています.すべてのスイッチやハイパーバイザはEBGPを利用して経路を交換するので,OSPFやISISなどのIGPを別途管理する必要はありません.また,RFC8950の技術を利用するBGP Unnumberedを採用しており,P2P Linkに対する明示的なIPv4アドレスの割当は必要ありません.最後に,自身のループバックIPからAS番号を計算して割り当てることで,データセンタでEBGPを運用する際によく知られているAS番号の割当問題を解決しています.より詳細な設計については,JANOG43で発表された資料を御覧ください.

https://www.janog.gr.jp/meeting/janog43/application/files/7915/4823/1858/janog43-line-kobayashi.pdf

FRRouting

上述したデータセンターネットワークにおいて,ハイパーバイザ等ではOSSルーティングプラットフォームであるFRRoutingを利用して,BGPを運用しています.FRRoutingはBGPを含む様々なルーティングプロトコルをサポートするデーモンスイートであり,現在も活発に開発,メンテナンスされています.私達LINEではこのFRRoutingを利用するだけでなく,必要に応じてコントリビューションを行っています.私達がFRRoutingに対して行っている活動については,以下の資料が参考になります.

https://github.com/FRRouting/frr
https://speakerdeck.com/line_developers/our-challenges-to-open-source-srv6-data-center-networking
https://engineering.linecorp.com/ja/blog/internship2021-frrouting/

ネットワークメンテナンス

LINEの多くのサービスは,プライベートクラウドのVerda上でホスティングされており,その規模は常に拡大し続けています.それに合わせて私達が開発/運用するインフラもスケールし続ける必要があり,それはネットワークにおいても同様です.そのため,上述したように私達は必要に応じてネットワークを刷新し,また必要な仕組みは独自に開発したり,OSSに機能拡張を行ったりしています.このような状況では,FRRoutingを含むネットワークのコンポーネントを頻繁にアップグレードする強い動機が存在します.しかし,ネットワークコンポーネントのアップグレードを行う最中も,大規模なワークロードが健常に動作し続けなければなりません.そこで私達はネットワークの可用性及びキャパシティを損なわずに,それぞれのコンポーネントをアップグレードする方法を考えてきました.

現状では,CLOSトポロジーのスイッチをアップデートする際に,アップデート対象を迂回する経路を設定するようなメカニズムを運用しています.しかし,このような独自のメカニズムはアドホックな対応としては十分ですが,理解するのに時間がかかるだけでなく,保守コストも高くなります.一般的なCLOSトポロジーにおいて,レイヤー間のフォワーディングはECMPによって負荷分散されるため,1台のダウンによって全断が発生するわけではありません.しかし,私達が意図してBGPを再起動する場合,特にシステムアップデートの場合にはリンク障害等が起こっていないため,再起動後は正常に動作を再開できると仮定できます.この場合,再起動中にフォワーディングを継続し続ける方がキャパシティの観点で優れています.

そこで私達は標準化されているルーティングプロトコルの仕様の利用することを検討し始めました.ルーティングプロトコルに規定されている技術を利用することで,独自に複雑なアルゴリズムを設計する必要がなくなり,保守コストを削減することができます.また,新しくチームに入ってきたメンバーも比較的素早くキャッチアップできる利点もあります.

BGPではOSPF等と同様に,ルータの再起動中,周囲からトラフィックを引き込んでフォワーディングを継続する仕組みであるGraceful Restart Mechanismが定義されています(RFC4724).この仕組みを利用して,FRRoutingのアップグレード中も,P2Pで接続するすべてのノードに対するECMPを継続するのがねらいです.しかし,現状BGP Graceful Restartを運用した経験はなく,私達の要件にマッチするかどうか不安でした.また,実際に検証してみると,私達の用途でBGP Graceful Restartを利用する際に不都合となる挙動を確認しました.そこで私はBGP Graceful Restartの挙動とユースケース,さらにFRRoutingを含む各OSSルーティングプラットフォームの動作確認を行い,整理することにしました.

BGP Graceful Restart Mechanismの概要

BGP Graceful Restart Mechanismは,以下2つのRFCによって定義されています.このセクションではこれらのRFCに対する私の解釈を,具体的なユースケースに沿って簡単に紹介します.RFCの内容をすべて解説するわけではありませんのでご了承ください.

BGP Graceful Restartは,再起動するルータと,そのネイバーの双方がサポートすることで実現されます.大まかな動作の流れは次の通りです.

  • BGP Capability Negotiation時にGraceful Restart Capabilityを交換する
  • 通常通り自身の持つ経路を広報しあう
  • BGP Restartを行い,BGP sessionが切断されている最中に双方でRIB/FIBの内容を保持する
  • BGP Sessionの再確立を行い復帰する

ref: https://datatracker.ietf.org/doc/html/rfc4724#section-4

ここでは例として,以下のような検証ネットワークを考えます.ここでR2はRFC4724で定義されているRestarting Speaker,つまり再起動するルータです.一方R1/R3はReceiving Speaker,つまり再起動を支援するルータとして動作させます.また,図では省略していますが,R1/R3ではconnected routeの再配布,graceful-restartの有効化を設定しておきます.なお,configはCisco IOSのものを参考にしています.

BGP Capability Negotiation

まずはじめに,”自身がGraceful Restartをサポートすること”をBGP Capability Negotiation時に通知します.双方のBGP SpeakerがGraceful Restart Capabilityを広報しあうことで初めて動作します.以下にその様子を示します.ここではR1-R2間にフォーカスします.R1が送るCapabilityにAFI/SAFI/Flagsのtupleを含めていませんが,これは”Receiving Speakerとしてのみ動作する場合に使える特別な形式である”とRFC4724で定義されています.実際にはReceiving Speakerとして動作する場合でも,AFI/SAFI/Flagsのtupleを含めることは可能ですが,ここでは簡潔にするために省略しました.後述するGoBGPというOSSでは明示的にReceiving Speaker Modeを設定でき,その場合にはこのようなCapability Formatで送信します.

Graceful Restart Capability(Capability Code=64)はRFC4724で定義されています.以下にGraceful Restart Capabilityの内容を示します.なお,BGP CapabilityはTLV(Type/Length/Value)の形式を取る為,実際にはこの内容の前にCapability CodeとLengthが含まれる点に注意してください.ここで, L はAFI tupleの数を表します.L は (<Capability Length> – 2) / 4 という計算式で求められます.

ref: https://datatracker.ietf.org/doc/html/rfc4724#section-3

Field NameLengthDescription
Restart State1 bitこのCapabilityを送信したBGP Speakerが再起動後かどうかを表す
Graceful Notification Support1 bitRFC8538の機能をサポートしているかどうかを表す
Unused2 bits未使用領域
Restart Time in Seconds(sec)12 bits再起動後,BGP sessionが再確立されるまでの推定秒数を表す
Supported AFI Tuples* 32bitsAFI(16 bits) / SAFI(8 bits) / Flags(8 bits)のtupleが連続したもの
指定されたAFI/SAFIのForwarding Stateを保持する機能をサポートしている場合に含まれる
Flags フィールドの最上位ビットは,“実際にForwarding Stateが保持されたか”を表す

今回の例では,R2がIPv4/Unicastに関するForwarding Stateの保持をサポートすることを示します.また,現在はどちらのSpeakerも再起動されていないので,Restart Stateは設定されていません.Forwarding Bitも同様に,まだR2が再起動していないので使用されません.

BGP Initial Route Exchange

BGP sessionが確立されると,RFC4271(BGP-4)で定義されているように,通常の経路広報を行います.その様子を下図に示します.今回の例では,S1/R1が接続する 192.168.10.0/24と,R2/R3が接続する10.255.20.0/24の経路が広報されます.図では R2のRIB/FIBについて注目していますが,実際にはR3にも 192.168.10.0/24 に対するルールが存在します.また,R1には 10.255.20.0/24 に対するルールが存在します.ここまでで,S1-R3間でL3到達性が確保されます.

BGP Restart

通常,Graceful Restartを無効化した状態でBGPを再起動すると,TCP Connectionの切断,もしくはBGP NOTIFICATIONの送信(とTCP Connectionの切断)が行われます.Peerではこれを検出し,RFC4271で定義されているBGP FSMに従って,そのConnectionで受け取っていた経路をBGP RIBから削除します.これは,FIB entryも同期して削除されることを意味します.また,session stateをIdleに設定します.TCP Connectionの切断が検出できない場合でも,KEEPALIVEの送信が行われないためにHold Timerが経過し,最終的にはTCP Connection closeが発生します.この場合,それぞれのルータは自身が接続するネットワークの情報のみを持つようになるため,L3到達性はそれぞれのセグメントに閉じます.これは,S1-R3間のoverallな疎通性が失われることを意味します.

一方,BGP Graceful Restartが有効になっている場合に,Restarting Speakerが再起動する場合について以下の図をもとに解説します.この場合はRFC4271の挙動と異なり,Restarting Speakerは自身がサポートするAFI/SAFIのフォワーディングルールを保持します.また,Receiving Speaker側も,Restarting Speakerから受け取ったCapabilityに含まれるそれぞれのAFI/SAFIについて,対応する経路を保持し,”STALE”経路としてマークします.STALE経路であるかどうかはフォワーディングに影響を与えないので,L3到達性は保持された状態になります.

ref: https://datatracker.ietf.org/doc/html/rfc4724#section-5

余談ですが,TCPの実装によっては,Receiving Speaker側でTCP Connectionの切断を検出できないことがあります.この場合は,後述するBGP sessionの再確立時に送られてくるBGP OPENの受信をトリガーにして,古いTCP ConnectionをCloseします.このときはNOTIFICATIONを送信しません.この場合も同様にHold Timeの経過が行われるまでは経路が削除されないために,L3到達性は保持されます.このような挙動が正しく動作するように,RFC4724ではRestart Timeの値をHold Time以下に設定するよう推奨されています.

BGP Session Re-establishment

BGPの再起動後,通常の動作と同様に,再度BGP OPENが交換されます.上述した一回目のSession Establishmentと異なり,Graceful Restart Capabilityの内容が変化します.以下にその様子を示します.R2は自身が再起動したことを何らかの方法で検知し,Restart StateのBitを立てます.また,再起動中R1から受け取っていた経路のForwarding Stateを保持していたので,flagsのForwarding Bitを立てます.この内容は,R3に対して送信するOPENでも同様です.一方Receiving Speaker側は一回目のSession Establishmentと同様の内容を送信します.ちなみにReceiving Speakerは,(一回目に)Restarting Speakerから受け取ったCapability内のRestart Timeを監視して,もしその秒数以内にBGP Session Re-establishmentが行われなければ,STALE経路をすべて削除します.

ここで,双方が送りあったBGP OPENの間で,Restart State Bitの内容に違いが生じます.Restarting SpeakerはRestart State Bitを立てて送信しますが,このときすべてのPeerから,Marker of End-of-RIB と呼ばれる特別なBGP UPDATEを受け取るまで,自身の(2回目の)Initial Routing Updateを延期します.Marker of End-of-RIBについては後述します.厳密には,延期されるのは経路選択フェイズですが,これは”自身のLoc-RIBにinstallし,forwarding ruleとして利用する経路を選ぶ”ことを意味します.つまり”他のPeerに対しどの経路を送信するか決定できない”ため,Initial Routing UpdateにおけるUPDATEの送信が延期される,という仕組みです.

一方,Receiving Speaker側はRestart State Bitを立てずに広報します.この場合,Receiving SpeakerはPeerからMarker of End-of-RIBを待たずに,すぐ自分のLoc-RIB及びAdj-RIBs-Outを計算し,その内容をPeerに広報します.すべてのPeerからMarker of End-of-RIBを受け取るまで待機する,という処理は行いません.この細かな挙動の違いによってデッドロックが発生せず,Restarting Speaker側で適切に経路の復元が行われます.

Marker of End-of-RIBはRFC4724で定義されている特別なBGP UPDATEであり,Graceful Restart Capabilityを交換することで送信可能になります.End-of-RIBはアドレスファミリによって形式が異なります.上図に示されているように,IPv4 unicastの場合はBGP-4で定義されているBGP UPDATEの最小サイズになります.それ以外の場合は,空の MP_UNREACH_NLRI path attributeのみを含むBGP UPDATEとなります.

Restarting Speakerは,すべてのPeerからEnd-of-RIBを受け取った後,経路選択を実行し,実際に経路広報を行います.ここでは先程と同様 192.168.10.0/24 にフォーカスして解説しますが,R3が先程と同様にBGP UPDATE(及びEnd-of-RIB)を受け取ると,自身のLoc-RIBを更新します.ここで,すでにSTALE経路として登録されているものがあればその状態を更新します.ここまででBGP Graceful Restartの一連の動作が完了し,NSFが実現できます.

まとめ

このセクションでは,BGP Graceful Restartの動作原理について簡潔に説明しました.大まかにまとめると,”あるルータが再起動するまでの間フォワーディングを継続しつつ,そのルータが広報した経路をPeerに保持してもらうことでNSFを実現する技術”というものです.ここから,BGP Graceful Restartを利用する場合の前提条件を考察することができます.それを以下に示します.

条件説明
1 hop内のBGP Speaker間では接続性が担保されたままであることPeerとの連携が前提にあるので,再起動後もすぐにBGP sessionを正常に確立できることが前提
各ルータで,Control PlaneとForwarding Planeが独立していることBGPの再起動によってFIBの状態が影響を受けてはならない
具体的には,BGP RIBを参照できない状態にあっても,FIBの内容がフリーズされるようになっていなければならない再度Peerから経路をもらってくるまでの間,自身のRIBにはFIBで保持する経路がないタイミングもあるが,
そのような状態でもFIBはフリーズされ続ける必要がある
事前にBGP Speakerの双方でGraceful Restartの機能が有効であることEnd-of-RIBの送信やそれぞれの挙動はGraceful Restartの機能が有効になっていて初めて行われるため,
対象のルータを再起動する前の段階で,お互いはGraceful Restart Capabilityを交換する必要がある
厳密には,BGP sessionを確立する前段階の時点で設定されていなければならない

ここまでをまとめると,ネットワークコンポーネントをアップグレードをするような要件でBGP Graceful Restartを実行する場合には以下のような懸念事項が存在します.

懸念事項説明
アップグレードするコンポーネントとForwarding Planeの互換性アップグレード前後でForwarding PlaneのAPI互換性が壊れると,アップグレード後健常に動作することを保証できない
Graceful Restart機能を有効化するためのオペレーションBGP Capabilityを交換し直す必要があり,そのためのネットワークメンテナンスが必要になる
“意図しない再起動”時のハンドリングBGP Graceful Restartは”何らかの理由でBGPがダウン”し,自動的に再起動された場合にも動作するが,
その場合は前提条件1の”Peerとの接続性”が担保されていない可能性があり,
むしろ再起動するルータにフォワーディングしてほしくない場合もある

私は,調査によって判明したBGP Graceful Restartの特性や原理をもとに,より実践的な検証を行い,私達の要件に適応可能か判断することにしました.次のセクションからは具体的な検証結果と,その考察について紹介します.

各OSSの挙動検証

このセクションではいくつかのOSSルーティングプラットフォームを取り上げて,それぞれがBGP Graceful Restartをどのように実装し,提供しているのかについて解説します.私達はOSS Networkingに力を注いでおり,必要な機能が実装されていない場合はコントリビューションすることもあるため,現状のフィジビリティを知ることは重要です.また,私達のユースケースに適合しているかどうかを見極めるためにも,具体的なネットワークを仮定して検証する意義があります.特にBGP Graceful Restartについては”対向ルータと連携して初めて実現される”機能であり,各OSSが相互運用性を持っているかどうかは非常に重要です.

本検証の結果から述べると,それぞれのOSSに対し動作確認を実施し,以下のような結果が得られました.

OSS(バージョン)期待する動作
GoBGP v2.34.0 + Zebra v7.5.1確認できた
FRRouting(BGPd, Zebra) v8.2.2確認できなかった

tinetについて

本記事では各OSSの動作検証に tinet というツールを利用します.tinetは検証ネットワークを構成するノード/リンク/コンフィグ等を入力すると,ある仮想ネットワークを作成し,それぞれのネットワークノード(コンテナ)をそのとおりに設定するようなシェルスクリプトを生成します.コンテナイメージのタグを細かく指定できたり,ネットワークトポロジーを宣言的に定義できるので,仮想ネットワークの検証に再現性をもたせることができます.Docker Composeでもほぼ同様のことが実現できますが,そのうちネットワーク検証に必要な機能のみを抽象化して提供するものだと認識していただければと思います.

本記事ではあくまでもそれぞれのOSS側でどのようなコンフィグが必要なのか,という観点から説明します.そのため,tinetの扱い方や,スペックを定義する方法などは解説しません.使い方を知らなくても読み進められるようになっていますが,もし詳しく知りたい場合は以下のリポジトリを参照いただければと思います.

https://github.com/tinynetwork/tinet

GoBGP

GoBGPはNTT発のBGP実装であり,Goで記述されています.gobgpdと呼ばれるデーモンプロセスと,gobgpと呼ばれるCLIのやり取りがgRPCで実現されている点が特徴的です.FIB maniplationの機能をあえて作っておらず,また,BGPの機能に集中して開発されているため,コンパクトで扱いやすいという特徴があります.

今回は以下のような構成で検証します.FIB操作を行うため,Zebraを有効化したFRRoutingのcontainer上でGoBGPを動作させます.このとき,GoBGPがサポートするバージョンのFRRoutingを動かす必要があるので注意してください.

検証に用いる各コンポーネントの具体的なバージョンは以下のとおりです.

R1側のコンフィグを以下に示します.R1はRestarting Speakerとして動作するため,forwarding stateを保持するアドレスファミリを予め指定しておきます.また,S1への経路をR2に伝えるため,connected routeの再配布を設定しておきます.S1では route add default gw 192.168.10.1 のようにしてnexthopをR1に指定しておきます.

[global.config]
  as = 65001
  router-id = "10.255.10.11"
 
[[neighbors]]
  [neighbors.config]
    neighbor-address = "10.255.10.12"
    peer-as = 65002
  [neighbors.graceful-restart.config]
    enabled = true
    restart-time = 120
  [[neighbors.afi-safis]]
    [neighbors.afi-safis.config]
      afi-safi-name = "ipv4-unicast"
      [neighbors.afi-safis.mp-graceful-restart.config]
        enabled = true
 
[zebra]
  [zebra.config]
    enabled = true
    url = "unix:/var/run/frr/zserv.api"
    redistribute-route-type-list = ["connect"]
    version = 6

一方R2側のコンフィグは少し異なります.これはreceiving speakerとしてのみ動作させる場合に使えるもので,AFI tupleが存在しないようなGraceful Restart Capabilityが送信されます.

[global.config]
  as = 65002
  router-id = "10.255.10.12"
 
[[neighbors]]
  [neighbors.config]
    neighbor-address = "10.255.10.11"
    peer-as = 65001
  [neighbors.graceful-restart.config]
    enabled = true
 
[zebra]
  [zebra.config]
    enabled = true
    url = "unix:/var/run/frr/zserv.api"
    version = 6

ここで,以下のようなコマンドを実行して,動作を確認します.実行結果として,R1側でGoBGPデーモンを再起動し,R2とsession re-establishmentが行われるまでの間,pingが実行され続けることを確認できました.

R1$ ./gobgpd -f r1.toml    # r1-shell1
R2$ ./gobgpd -f r2.toml    # r2-shell1
R2$ ping 192.168.10.254    # r2-shell2
R1$ pkill -9 gobgpd        # r1-shell2
R1$ ./gobgpd -f r1.toml -r # r1-shell1

実際に,1回目のBGP Capability Negotiationで交換されたGraceful Restart Capabilityの内容を見てみましょう.ここではまずR1が送信した内容を示します.Graceful Restartの挙動の解説と同じように,Restart State BitとForwarding State Bitがそれぞれ0の状態になっているのがわかります.また,GoBGPのコンフィグで明記したとおり,IPv4/UnicastのAFI tupleが存在します.

続いてR2が送信した内容を示します.こちらはAFI tupleが存在しないためより少ない情報が表示されています.Wiresharkではこの形式を検出して,Helper modeでのみ使用されるものだと出力しています.実際,このようなCapabilityを送信するBGP Speakerはいかなるアドレスファミリに対してforwarding stateの保持を行わず,Peerの再起動に対して経路を保持する機能のみを提供するため,Receiving Speakerとして動作する場合にのみ用いることができます.

続いて2回目,R1側でGoBGPが再起動したあとのGraceful Restart Capabilityを見てみます.R2が送信する内容は変化しないので,R1側のみ以下に示します.実際に再起動が完了したので,Restart State Bitが立ちます.また,実際にforwarding stateを保持していたので,IPv4/Unicastに対応するForwarding State Bitも1に設定されています.

その後,R2からR1に対してMarker of End-of-RIBが送信され,最後にR1からR2に対してconnected routeの広報が行われます.これもRFC通りの挙動です.スクリーンショットでは,No.77がReceiving SpeakerのMarker of End-of-RIB, No.79がRestarting Speakerからの経路再広報に該当します.これによって,Receiving Speaker側でSTALE経路の更新が行われ,再起動前と同様にフォワーディングが継続されます.

FRRouting

FRRoutingは私達LINEが採用するOSSルーティングプラットフォームです.C言語で実装されており,BGP以外にも多くのルーティングプロトコルに対応している点が特徴です.各ルーティングプロトコルのデーモン,そしてFIBなどの資源を管理するコンポーネント(以後Zebra)がそれぞれLinuxプロセスになっており,それらが協調動作します.

BGP Graceful RestartはBGPd(BGPデーモン)とZebraの連携によって実装されています.ネイバーごとにBGP Graceful Restartを有効化する機能はサポートされておらず,またアドレスファミリごとにフォワーディングを保持するかどうか設定する機能もありません.

検証に用いる各コンポーネントの具体的なバージョンは以下のとおりです.

R1側では,FRRoutingを以下のように設定します.FRRoutingではアドレスファミリごとにpreserveする機能の有効化を選択できないため, address-family ブロックの外側で宣言しています.また,Receiving Speakerとして明示的に設定する方法はないので,R2側のコンフィグもあまり変わりません.

frr version 8.2.2_git
frr defaults traditional
hostname R1
no ipv6 forwarding
!
interface net1
 ip address 192.168.10.1/24
exit
!
interface net12
 ip address 10.255.10.11/24
exit
!
router bgp 65001
 bgp router-id 10.255.10.11
 bgp graceful-restart
 bgp graceful-restart preserve-fw-state
 neighbor 10.255.10.12 remote-as 65002
 !
 address-family ipv4 unicast
  redistribute connected
  neighbor 10.255.10.12 route-map RM1 out
 exit-address-family
exit
!
ip prefix-list PL1 seq 5 permit 192.168.10.0/24
!
route-map RM1 permit 10
 match ip address prefix-list PL1
exit
!
end

この環境で,R2からS1に対しPingを送信し続け,その間にR1のBGPdを再起動(SIGKILLの送信と,watchfrrによる再起動)させたところ,以下のような出力とともに失敗しました.これはR2側で経路の保持(及びが行われず,R1から受け取った経路を破棄してしまっていることに起因します.RFC4724ではいくつかのケースでReceiving Speakerが経路を破棄する条件が規定されていますが,そのいずれかに合致してしまっているか,FRRouting側の特性に起因するものだと推測されます.

また,R1のBGPdに対してSIGTERMを送った場合,即座にR2に対してBGP NOTIFICATIONが送信され,R2側ではこのNOTIFICATIONをトリガーに,即座にフォワーディングルールを削除してしまう,という挙動を確認しました.これもRFC4724でBGP FSMに加えられた変更とは異なる挙動だと考えられます.さらに,R1のZebraに対してSIGKILL/SIGTERMを送信した場合も,Zebraが再起動されたタイミングでR2側のフォワーディングルールが削除される挙動を確認しました.

R2$ ping 192.168.10.254
# omitted
64 bytes from 192.168.10.254: seq=45 ttl=63 time=0.088 ms
64 bytes from 192.168.10.254: seq=46 ttl=63 time=0.090 ms
64 bytes from 192.168.10.254: seq=47 ttl=63 time=0.086 ms
64 bytes from 192.168.10.254: seq=48 ttl=63 time=0.092 ms
64 bytes from 192.168.10.254: seq=49 ttl=63 time=0.086 ms
ping: sendto: Network unreachable

1回目のBGP Capability Negotiationで,R1が送信したGraceful Restart Capabilityの内容を見てみます.ここで,Restart State BitとForwarding BitがGoBGPの内容と異なることがわかります.

R2が送信した内容も同じく,GoBGPの内容と異なります.

この挙動は,RFC4724に記載されている内容と異なります.まずRestart Stateについてですが,RFC4724のSection 4.2には以下のような記載があります.要約すると,”Receiving Speakerが再起動したわけでないのなら,Restart State bitをsetしてはならない”という内容です.

In re-establishing the session, the “Restart State” bit in the
Graceful Restart Capability of the OPEN message sent by the Receiving
Speaker MUST NOT be set unless the Receiving Speaker has restarted.

cited from: https://datatracker.ietf.org/doc/html/rfc4724#section-4.2

また,BGP OPENの後送られるBGP UPDATEについても,Restarting SpeakerからMarker of End-of-RIBを送信しはじめるという違いを確認しました.RFC4724のSection 4.1では,以下のように記載されています.(a) の内容に絞って要約すると,”Restart State Bitを0にして送信してきたすべてのPeerからEnd-of-RIBを受け取るまで経路選択を延期する”という内容です.

However, it MUST defer route
selection for an address family until it either (a) receives the
End-of-RIB marker from all its peers (excluding the ones with the
“Restart State” bit set in the received capability and excluding the
ones that do not advertise the graceful restart capability) or (b)
the Selection_Deferral_Timer referred to below has expired.

本記事ではFRRouting v8.2.2を利用した検証結果をご紹介しましたが,実際にはv8.0.0やv8.1.0でも同様に検証を行いました.複数の試行回数のうちほとんどの場合で同様にR2側でフォワーディングルールの削除が行われてしまいましたが,まれにフォワーディングルールが削除されず正常に動作したという報告/確認もされているため,今回の検証ではFRRoutingがBGP Graceful Restartに対応しているかどうかを厳密に詰め切ることはできませんでした.今回セットアップした検証ネットワークの設定自体に不備があるケースもありますし,そもそも動作が不安定だったり,FRRouting側でバックポートが行われてバグが混入したりするようなケースも考えられるため,FRRoutingコミュニティに相談したり,より精密に検証をすすめる必要があると考えます.

今後の展望

BGP Graceful Restartのユースケース調査

今回の調査によってBGP Graceful Restartがどのように動作し,また,どのような用途に使用できるのかについて整理することができました.しかし,当初の目的である”システムアップデート”に対してBGP Graceful Restartを適用することが適切かどうか,という議論をする必要があります.例えば,BGP Graceful Restartは”意図しない再起動”のように何らかの理由でBGP Session Downが行われた場合にも動作するように設計されていますが,何らかの理由で意図せずSession Downが行われた場合には経路収束が行われて再起動するルータを経由しないように動作してほしいというモチベーションもあるため,Graceful Restartの運用は慎重に行う必要があります.以上の理由で,私達のチームでBGP Graceful Restartを利用するまでにはまだまだ調査が必要だということが明らかになりました.

FRRoutingに対するコントリビューションについて

BGP Graceful Restartを私達の要件に適用するかどうかに限らず,FRRoutingの挙動についてその原因や想定挙動を追求する高いモチベーションが存在します.コミュニティに相談した上で,もし改良の余地がある場合はコントリビューションすることも考えられます.一方で,先述したように現在のFRRoutingではZebraを中心としたGraceful Restartで動作しているため,その設計思想と折り合わせながらコントリビューションを行うのは簡単ではありません.私達としては,BGPdだけではなくZebraを含む他デーモンもすべてアップデートすることで,Zebra APIの互換性を保ちつつアップグレードすることができますし,管理もしやすくなりますが,FRRoutingの設計上これが正しくワークするか,そしてやるべきかどうかの議論が必要です.これはマルチプロセスで動作する大きなデーモンスイートという,FRRoutingのアーキテクチャ設計自体に対する深い議論を意味しています.このコストを許容するか,別のソリューションを展開するかについて考える必要があります.

自身がOriginatorではない場合の動作検証

本記事での検証ネットワークでは,経路のOriginatorとRestarting Speakerが同じような場合を想定しましたが,Graceful Restartの動作原理的には,概要解説の例で紹介したようにRestarting SpeakerがあるEBGP Peerから受け取った経路を更に広報するようなケースでも動作するはずです.しかし実際には,GoBGPを用いた検証においても,Restarting Speaker側でフォワーディングルールの保持が行われないという現象を観測しています.私の想定する動作が不正確なのか,GoBGP側にそういうロジックが存在するのか,それともZebra側がコネクションをハンドルして,そういう挙動を示しているのかが明瞭にわかっていませんが,実際にはネットワーク内のいかなるノードでもシステムアップデートを行いたいことを考えると,これが正しく動作するかどうか知ることは非常に大切です.

最後に

本記事ではBGP Graceful Restartの動作概要を解説し,各OSSの動作確認を行い,結果を整理しました.実際に動作する大規模なシステムを前提に,私達の要件について,それが現実的に実現可能か,そのコストをかける価値があるかなどの検討を行う必要があり,今回はそれを実感する非常に良い機会となりました.

私事ではありますが,私は2022年の3月末でLINEのアルバイト契約が終了するので,今回のBGP Graceful Restartが最後のタスクになりました.以前LINE Engineering Blogに投稿した仮想ルータのメンテナンス機能を”独自インフラソフトウェアのエンジニアリング”とするならば,今回はRFCとOSS,つまり”オープンな技術の調査と検証”だと考えることができ,この双方に取り組む機会をいただけたのは貴重な経験だったと感じております.前回の記事と重ねてになりますが,Verda室のネットワーク開発チームの方々を始めとするたくさんの方のご支援によってアルバイト活動を無事終えることができました.特にメンターの城倉さんからは様々なことをご教授いただきました.ここで改めて感謝いたします.