元バンドマンITエンジニアの語り場

技術とか趣味とか日々の至福と鬱憤とか

【Network】BGPの考慮点

BGPを実装する上で押さえておくべき考慮点についていくつか整理してみました。
すでにBGPを動かしているトポロジにおいては、新規にBGPピアを追加するような場合には既存の設計を踏襲することが多いかと思うのですが、一度立ち止まって、設計の際に考慮するべきBGPの仕様について備忘として記しておきます。

いつものように、何の脈略もないですがただの備忘なのでご了承ください。

1.NEXT_HOPパスアトリビュートの特性について


NEXT_HOPは、宛先ネットワークに対するネクストホップのIPアドレスを示すアトリビュートです。 Well-known mandatoryタイプなので、全てのBGPにて使用可能かつBGPメッセージに必ず付与される情報です。 ネクストホップを示すのですが、BGPはAS間ルーティーングプロトコルなので、ネクストホップは隣のASに到達するためのIPアドレスということなります。
なので、BGPスピーカはNEXT_HOPアトリビュートを隣のASのBGPピア(EBGP)にアドバイタイズする時は、自身のIPアドレスを通知します。
ただし、アドバイタイズする相手が自ASのBGPピア(IBGP)の時はNEXT_HOPアトリビュートを変更せずそのまま通知します。

f:id:Mickey6:20210912122409p:plain 図1

例えば図1のようなEBGPとIBGPで構成されているトポロジがあった場合、R1がR2に対してAS10内に存在する「10.0.0.0/8」のネットワーク情報をアドバタイズする際はNEXT_HOP属性に自身のIP(1.0.0.1)をセットします。 ついで、R2がR3に対して「10.0.0.0/8」のネットワークをアドバタイズする際は、R2とR3はIBGPなので、NEXT_HOP属性はそのまま(1.0.0.1のまま)送信します。 R3がR4に「10.0.0.0/8」をアドバタイズする際は、EBGPなのでNEXT_HOP属性を「3.0.0.1」に書き換えるわけです。

この時重要なのが、R3はNEXT_HOPアドレス(ここでいう1.0.0.1)に到達可能なルーティング情報を持っている必要があるということです。もしR3が1.0.0.1への経路情報を持っていない場合、「10.0.0.0/8」宛のパケットはR3で破棄されてしまうのです。

そしてこのIBGPにおけるNEXT_HOP到達性問題を解決する方法は一般的に2つあります。

①NEXT_HOPアドレス向けのスタティックルートを設定する

図1でいうとR3に対して以下を設定することでR3のルーティングテーブルに「1.0.0.1」宛の経路情報が載るためAS10のNEXT_HOPアドレスへの到達性が生まれるわけです。

R3(config) # ip route 1.0.0.1 255.0.0.0 2.0.0.1

ただし、外部ASと接続するリンクのネットワークアドレスをAS内にアドバタイズするのはセキュリティ的によろしくないので、通常は次の方法を取ります。

②NEXT_HOPアドレスを変更する

二つ目はEBGPルートを受信したルータがIBGPピアにアドバタイズする際にNEXT_HOPを変更するよう明示的に設定変更をする方法です。
AS境界ルータで[neighbor next-hop-self]を設定すると、IBGPのBGPピアに経路情報をアドバイタズする際にNEXT_HOP属性を自身のIPに書き換えます。

具体的な設定方法は以下。 

R2(config) # router bgp 20
R2(config-router) # neighbor 1.0.0.1 remoto-as 10
R2(config-router) # neighbor 2.0.0.2 remoto-as 20
R2(config-router) # neighbor 2.0.0.2 next-hop-self

上記の設定をすることで、R2はIBGPピアであるR3(2.0.0.2)にアドバイタズする際、NEXT_HOP属性に「2.0.0.1」をセットするようになるので、R3に対して「1.0.0.1」向けのスタティックを設定する必要がなくなるというわけです。

ちなみに、NEXT_HOP属性の定義には例外もあります。

f:id:Mickey6:20210912131227p:plain 図2

図2のようなマルチアクセスネットワーク上のEBGPピアにルートをアドバタイズする場合はNEXT_HOP属性は変更されません。
R1とR2間はIBGPピア、R2とR3間はEBGPピア関係となっているとして、R1が「10.0.0.0/8」のネットワーク情報をIBGPピア(R2)にアドバタイズするときはNEXT_HOP属性に「20.0.0.1」をセットします。そしてR2がEBGPピアであるR3に「10.0.0.0/8」をアドバタイズする時はNEXT_HOP属性を「20.0.0.2」にするのではなく「20.0.0.1」のまま送ります。これは、R3とR1と同じマルチアクセスネットワーク上にある時の動作となり、このような動作となることで、R3から「10.0.0.0/8」宛のパケットはR2を経由せずに直接R1に転送されるため効率的にパケットの配送を行うことができるというわけです。
このパターンは見かけたことないですけどね。

2.networkコマンドの注意点


BGPスピーカがピアに対してルート情報をアドバタイズするためには「network」コマンドでアドバイタイズしたいネットワークを指定する必要があります。

R2(config-router) # network <xx.xx.xx.xx> [mask <xx.xx.xx.xx>]

maskオプションを付けるとサブネットアドレスや集約したアドレスをアドバタイズし、maskオプションを付けないとクラスフルネットワークをアドバタイズします。
networkコマンドに関して、恥ずかしながら最近知ったことがあり、それはnetworkコマンドで指定するネットワークは、ルーティングテーブル内のエントリーとサブネットマスクも含めて完全一致している必要があるということです。
なので、集約されたCIDRブロックをBGPピアにアドバタイズしたい場合が、CIDRブロックがルーティングテーブルにエントリされるように、以下の方法でスタティックルートの設定をする必要があります。

R2(config) # ip route 192.168.0.0 255.255.0.0 null 0

アドバイタズしたいCIDRへのスタティックルートをNull 0インターフェイスに対して設定することで、ルーティングテーブルにもCIDRブロックがエントリされ、BGPルートとしてもアドバタイズされるようになるというわけです。
実際はこのように集約されたルートをBGPピアにアドバタイズする場合は、aggregate-addressコマンドで経路集約をするのが一般的かと思います。

3.BGPピア間接続の問題


マルチホーム構成でIBGPピアに冗長リンクがある場合、neighbor remote コマンドで指定するIPアドレスに冗長リンクのうちいずれか1つのみを使用した場合、そのリンク障害が発生した時にTCP接続も切れBGPネイバーも切断されます。

f:id:Mickey6:20210912192000p:plain
図3

図3のような構成の場合で、AS10にてIGPにOSPFを使用していたとする。
R1とR2間の「10.1.1.0/24」のリンクでのみBGPピアを構成していた場合、このリンクで障害が発生したらBGPピアが切断されてしまいます。

冗長リンクがあるにも関わらずBGPピアはSPOFとなっています。
この問題を解決する方法は二つあります。

①冗長リンクでもBGPピアを確立する

図3の場合、R1にneighbor 10.1.1.2 remote-as 10neighbor 10.3.3.1 remote-as 10の二つのコマンドを設定することで、片方のピアが切断されたとしてももう片方のピアでBGP関係を維持することができます。
ただし、この方法は二つのBGPセッションでBGPルート情報が交換されるため帯域やメモリを多く使用するためルータへの負荷がかかり適切ではない。

②ループバックインターフェイスでBGPピアを確立する

二つ目はループバックインターフェイスを利用してピア関係を確立する方法です。 この場合はいずれかの物理リンクが障害で切断されてもループバックインターフェイスは ダウンしないため、一つのリンクで冗長性を保ちながらも余計なリソースを消費せずにすみます。

ループバックインターフェイスを利用してBGPピアを組む際に注意すべきは、受信したBGPパケットの送信元アドレスと自身がneighbor remote-asで指定したアドレスが一致しなければならない というBGPのルールを守ることです。
neighbor remote-asコマンドでループバックのアドレスを指定しても、受信したBGPパケットの送信元アドレスはそのパケットを出力した物理インターフェイスのアドレスになるので、アドレス不一致となりBGPピア関係を確立できません。

なので、以下のような設定をすることでBGPパケットの送信元アドレスを明示的にループバックのアドレスに変更することが必要となります。

R1(config) # router ospf 1
R1(config-router) # network 1.1.1.1 0.0.0.0 area 0
R1(config) # router bgp 10
R1(config-router) # neighbor 2.2.2.2 remoto-as 10
R1(config-router) # neighbor 2.2.2.2 update-source loopback 0

OSPFの冗長リンク経由でもお互いのループバックアドレスへ到達できるよう、OSPFで自身のループバックアドレスをアドバタイズする必要があるのがポイントです。

ちなみに、EBGPピアでもループバックインターフェイスを使用することができます。(図4)

f:id:Mickey6:20210912195540p:plain 図4

ただし、EBGPピアは直接接続されていることが前提となっているので、EBGPピア向けのBGPパケットのTTLはデフォルトで「1」がセットされています。そのため直接接続されていないループバックインターフェイスに到達する前にTTL切れとなり、このままではループバックインターフェイスを利用したEBGPピアを確立することができません。
なので、BGPパケットのTTLを明示的に増やしてあげる必要があります。

コマンドは以下です。

R1(config) # ip route 2.2.2.2 255.255.255.255 10.0.0.2
R1(config) # ip route 2.2.2.2 255.255.255.255 20.0.0.2
R1(config) # router bgp 10
R1(config-router) # neighbor 2.2.2.2 remoto-as 20
R1(config-router) # neighbor 2.2.2.2 update-source loopback 0
R1(config-router) # neighbor 2.2.2.2 ebgp-multihop 2

R1からR2のループバックアドレスへ到達できるようスタティックルートを設定した上でR2とEBGPピアを確立します。
update-sourceで自身が送信するBGPパケットの送信元アドレスをループバックアドレスに変更した上で ebgp-multihopコマンドでTTLの値を「2」に変更することで、BGPパケットがR2のループバックアドレスまで到達できるようになります。

そろそろ力尽きてきたのでここまでにしますが、BGPを管理する上での考慮点はまだまだあるので、また時間がある時にまとめてみます。