実際の現場で起きる問題:物理リンクはUpなのに通信が全滅するケース
深夜2時、バックエンドAPIクラスタ全体のタイムアウトによりPagerDutyのアラートが鳴り響きました。急いで飛び起き、データセンターにあるUbuntuゲートウェイへSSH接続しました。FRRouting上のBGPおよびOSPFセッションは、依然としてEstablishedやFullの正常ステータスを示しています。しかし、実際のトラフィックは完全に0 Mbpsへと急落していました。
原因は、中間スイッチにある光メディアコンバーターのマイクロコントローラーがフリーズしたことでした。サーバーのNICに接続されたポートには給電が続いており、リンクLEDも点灯したままです。そのためLinuxカーネルは背後の回線が途絶していることに全く気づけず、結果としてすべてのパケットがブラックホールへと吸い込まれ続けていました。
このボトルネックは、各ルーティングプロトコルにおけるデフォルトのキープアライブ機構に起因します。
- OSPF: Helloタイマーが10秒、Deadタイマーは最大40秒。ネットワークの迂回切り替えに約1分近くかかります。
- BGP: デフォルトのKeepaliveは60秒、Holdタイマーは180秒にも及びます。つまり、BGPルーターがneighborのダウンを認識するまでに丸々3分間トラフィックが失われ続けます。
OSPFのHelloタイマーを1秒まで短縮すればよいのでしょうか? 可能ではありますが、数十台のneighborが存在する場合、ルーターのCPU負荷が急増して動作が不安定になります。そもそもOSPFはミリ秒単位の検知を想定して設計されていません。そこで必要となるのがBFD(Bidirectional Forwarding Detection – RFC 5880)です。
BFDはUDP(single-hopはポート3784、echoはポート3785)経由でデータプレーン層において独立して動作します。BFDパケットは数十バイト程度と極めて軽量で、CPU消費も0.1%未満に抑えられます。その唯一の役割は、ミリ秒間隔で高頻度のハンドシェイクを行うことです。パケットを連続で3回ロストすると、BFDは即座にOSPF/BGPのneighborセッションを切断します。これにより、ルーティングテーブル全体が300ms未満で予備経路へと高速収束(コンバージェンス)します。
FRRoutingのインストールとBFDデーモンの有効化
FRRouting(FRR)は現在、Quaggaに代わるLinux向けの標準的なルーティングスイートです。bfddの全機能を利用するには、Ubuntuのデフォルトaptリポジトリではなく、FRR公式リポジトリからインストールすることを推奨します。
# 1. FRR公式リポジトリの追加
sudo apt update && sudo apt install -y curl gnupg2 lsb-release
curl -s https://deb.frrouting.org/frr/keys.asc | sudo gpg --dearmor -o /usr/share/keyrings/frrouting.gpg
echo "deb [signed-by=/usr/share/keyrings/frrouting.gpg] https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable" | sudo tee /etc/apt/sources.list.d/frr.list
# 2. FRRパッケージのインストール
sudo apt update
sudo apt install -y frr frr-pythontools
デフォルトでは、FRRの中核デーモンであるzebraのみが有効になっています。/etc/frr/daemonsファイルを開き、BFD、OSPF、BGPを有効化します。
sudo nano /etc/frr/daemons
対応するフラグを以下のように変更します。
zebra=yes
bgpd=yes
ospfd=yes
ospf6d=no
ripd=no
bfdd=yes
サービスを再起動し、システムの自動起動を有効にします。
sudo systemctl restart frr
sudo systemctl enable frr
OSPFおよびBGP向けのBFD設定手順
検証構成は2台のゲートウェイで構成されます。中間L2スイッチを経由して、Router-A(eth1: 10.10.10.1/30)とRouter-B(eth1: 10.10.10.2/30)が接続されています。
1. vtyshへのアクセス
FRRの統合設定シェルを起動します。
sudo vtysh
2. BFD peerの定義
Router-A側で、送受信間隔を100ms、ロスト検出倍率(detect-multiplier)を3に設定します。ルーターが3パケット連続でロスト(計300ms)した場合、リンク断と判定されます。
configure terminal
bfd
peer 10.10.10.2 interface eth1
detect-multiplier 3
receive-interval 100
transmit-interval 100
exit
exit
3. OSPFインターフェースへのBFDの適用
neighborが接続されているインターフェースに直接ip ospf bfdコマンドを追加します。
configure terminal
interface eth1
ip address 10.10.10.1/30
ip ospf area 0
ip ospf bfd
exit
router ospf
router-id 1.1.1.1
network 10.10.10.0/30 area 0
exit
ip ospf bfdを1行指定するだけで、OSPFプロセスが自動的にbfddへ登録し、eth1上のneighbor監視を開始します。
4. BGPへのBFDの統合
BGP(eBGPまたはiBGP)の場合、対象の各neighborに直接BFDを関連付けます。
configure terminal
router bgp 65001
neighbor 10.10.10.2 remote-as 65002
neighbor 10.10.10.2 bfd
exit
5. Router-B側での対向設定
双方向のBFDハンドシェイクを確立するため、Router-Bでも同様に設定を行います。
configure terminal
bfd
peer 10.10.10.1 interface eth1
detect-multiplier 3
receive-interval 100
transmit-interval 100
exit
exit
interface eth1
ip address 10.10.10.2/30
ip ospf area 0
ip ospf bfd
exit
router ospf
router-id 2.2.2.2
network 10.10.10.0/30 area 0
exit
router bgp 65002
neighbor 10.10.10.1 remote-as 65001
neighbor 10.10.10.1 bfd
exit
# 設定をディスクに保存することを忘れずに実行
write memory
注意: write memoryコマンドは、現在のランタイム設定を/etc/frr/frr.confファイルに書き込みます。この手順を省略すると、再起動時に設定内容がすべて失われます。
ステータス確認と擬似障害テスト
設定保存後、本番環境へ投入する前にBFDセッションが正常に確立されているか確認します。
1. BFDセッションの確認
vtyshでpeerステータスのサマリーを表示します。
show bfd peers
想定される出力結果:
FRR-Router-A# show bfd peers
BFD Peers:
peer 10.10.10.2 interface eth1
ID: 219481023
Remote ID: 198302194
Status: up
Uptime: 0 hour(s), 12 minute(s), 18 second(s)
Diagnostic: ok
Remote diagnostics: ok
Receive Interval: 100ms
Transmit Interval: 100ms
Remote Receive Interval: 100ms
Remote Transmit Interval: 100ms
Status: upおよび100msの間隔設定が表示されていれば、両ルーター間でBFDパケットが正常に送受信されていることが確認できます。
2. OSPFおよびBGPがBFDと連携しているかの確認
OSPF neighborの詳細情報を確認します。
show ip ospf neighbor detail
出力内にBFD enabled: status is Upという行が表示されます。続けてBGPを確認します。
show ip bgp neighbors 10.10.10.2
BFD enabled: yes, status: Upが表示されていれば連携成功です。
3. 障害シミュレーションテスト(カオステスト)
中間ネットワークの光ファイバー障害時の挙動を検証するため、Router-B上でiptablesを用いてBFDのUDPポート3784を遮断し、通信途絶をシミュレートします。
# Router-B側で片方向のBFDパケットをドロップ
sudo iptables -I INPUT -p udp --dport 3784 -j DROP
直ちにRouter-Aでログ監視画面を開きます。
show log monitor
即座に以下のようなログが出力されます。
%BFD_PEER_DOWN: BFD peer 10.10.10.2 down
%OSPF-5-ADJCHANGE: Nbr 2.2.2.2 on eth1: Reset (BFD down)
%BGP-5-ADJCHANGE: neighbor 10.10.10.2 Down BFD session down
ドロップ実行から正確に300ms後、OSPFとBGPは即座にneighborを切断しました。ルーティングテーブルは自動的にバックアップ経路へ切り替わり、これまでのように40秒から3分間もトラフィックが停止することはなくなりました。iptablesルールを削除(sudo iptables -D INPUT 1)すると、数ミリ秒でBFDの再ハンドシェイクが行われ、ルーティングがスムーズに復旧します。
