読者です 読者をやめる 読者になる 読者になる

LGTM

Looks Good To Me

inet-henge にいくつか機能を足した

inet-henge というネットワーク図生成ライブラリーに

  • リンク太さを変えられる
  • リンク両端に加え,中央にラベルを置ける

地味な機能を足した.

github.com

inet-henge とは?

JSON データを元にネットワーク図を生成してくれる js ライブラリー.オートレイアウトする.

このようなコンセプトで作っている.

  • 自動更新
    • 入力少なく
    • このくらいの外部データから生成したい.手動でレイアウトしたくない
{
  "nodes": [{ "name": "A" },{ "name": "B" }],
  "links": [{ "source": "A", "target": "B" }]
}
  • 美しくなくていい.見てわかる範囲でやってくれれば OK

  • 動かしたい

    • 運用しながら / 設計しながら見る.デバイスやPOP 単位でノードを動かしたい
    • レイアウトは保存しなくてもいい.再計算しても毎回結果が同じならOK
    • ズームしたい.ある程度拡大したときのみ細かい情報を表示したい
    • ブラウザでネットワーク図を表示したその画面で実現したい.ドキュメント内でもやりたい

デモ

youtu.be

Shownet 2016 Network

リンク太さを変える

こんな感じでメタデータを定義しておいて 👇

  "links": [
    { "source": "Node 1", "target": "Node 2", "meta": { "bandwidth": "1G" }},
    { "source": "Node 1", "target": "Node 3", "meta": { "bandwidth": "10G" }},
    { "source": "Node 2", "target": "Node 3", "meta": { "bandwidth": "100G" }}
  ]

こんな感じで描画する.👇 メタデータをもとにSVGstroke-width を返す関数を渡す.

var diagram = new Diagram('#diagram', 'index.json');
diagram.link_width(function (link) {
  if (!link)
    return 1;  // px
  else if (link.bandwidth === '100G')
    return 10; // px
  else if (link.bandwidth === '10G')
    return 3;  // px
});
diagram.init('bandwidth');

何も返さない場合はSVG のデフォルト = 1px になる.

リンクにラベルを置く

こんな感じのリンク情報があったとすると 👇

  "links": [
    {
      "source": "Node 1", "target": "Node 2",
      "meta": {
        "bandwidth": "10G",
        "intf-name": { "source": "interface A", "target": "interface B" }
      }
    }
  ]

こう書くことで 👇

new Diagram('#diagram', 'index.json').init('bandwidth', 'intf-name');

こうなる 👇

スタイルを変える

(追加機能ではないけれど) inet-henge はSVG を出力するので,CSS でスタイルを変えることができる.

.node rect {
  fill: #25271e !important;
  transform: scale(2) translate(-13px, -5px);
}

.node text {
  font-family: sans-serif;
  fill: #fff;
  font-size: 20px;
  transform: translate(0, 5px)
}

.link {
  stroke: #d0e799;
  stroke-opacity: 1;
}

今後

  • LAG をうまく表現できない.なんとかしたい
  • ノード / リンクが増えたときのパフォーマンスが問題.アルゴリズムの調整,計算結果の再利用など考えないといけない

もしご意見などありましたら…気軽にお声がけください!

Large BGP Community がやってくる前に,Community マッチをおさらいしよう

2017 / 02月,BGP Large Communities Attribute (RFC8092) がRFC 化された.

これは新たに 4Bytes:4Bytes:4Bytes のBGP Community を使えるようにするもので,既存の

  • BGP Community (RFC1997) - 2Bytes:2Bytes
  • Extended BGP Community (RFC4360) - 4Bytes:2Bytes もしくは 2Bytes:4Bytes

に比べて空間を広く使うことができる.BGP Community の先頭2バイト(もしくは4バイト) はGlobal Administrator と呼ばれ,ふつうは事業者のグローバルAS番号をあてる.加えて操作対象のAS番号,たとえば顧客のAS番号をBGP Community に含めたいケースがあり,これまでのBGP Community では空間が足りなかった.

近々BGP Community 空間が拡張されるにあたり「いま設定しているBGP Community 正規表現だいじょうぶなんだっけ?」と思って,いくつかの実装でマッチ方法を復習した.

Community マッチ方法

Juniper JUNOS

  • Community 表現(横) がマッチ対象(縦) にマッチするか
  • アルファベットはBGP Community を10進で表現したときの1ケタ.4ケタ = 2Bytes,6ケタ = 4Bytes
  • Large BGP Community 実装はなさそう
⬇️対象 \ 表現➡️ bbb:xxx bbb:xxx.* bbbL:xxx.*
bbb:xxx ⭕️ する ⭕️ する ❌ しない
abbb:xxx ❌ しない ⭕️ する ❌ しない
target:abbb:xxx ❌ しない ⭕️ する ❌ しない
target:aaabbb:xxx ❌ しない ❌ しない ⭕️ する
bbb:xxxy ❌ しない ⭕️ する ❌ しない

想像するに

  • L キーワードの有無で長さマッチ
  • \d+:\d+ パターンであれば^\d+:\d+$ と解釈.それ以外はそのまま

Cisco IOS-XR

  • 正規表現(横) がマッチ対象(縦) にマッチするか
  • アルファベットはBGP Community を10進で表現したときの1ケタ.4ケタ = 2Bytes
  • community-set の例.ほかにextcommunity-set キーワードがある
  • Large BGP Community 実装はなさそう
⬇️対象 \ 表現➡️ bbb:xxx bbb:* ios-regex ‘bbb:xxx’ ios-regex ‘bbb:xxx.*’
bbb:xxx ⭕️ する ⭕️ する ⭕️ する ⭕️ する
abbb:xxx ❌ しない ❌ しない ⭕️ する ⭕️ する
target:abbb:xxx ❌ しない ❌ しない ❌ しない ❌ しない
target:aaabbb:xxx ❌ しない ❌ しない ❌ しない ❌ しない
bbb:xxxy ❌ しない ⭕️ する ⭕️ する ⭕️ する

想像するに

  • community-set キーワード,extcommunity-set キーワードで分離.異なるものにマッチしない
  • ワイルドカードは 文頭 / 文末 を前提にしてマッチ
  • 正規表現は 文頭 / 文末 でなくてもマッチ

gobgp

  • 正規表現(横) がマッチ対象(縦) にマッチするか
  • アルファベットはBGP Community を10進で表現したときの1ケタ.4ケタ = 2Bytes,6ケタ = 4Bytes
  • community の例.ほかにext-communitylarge-community キーワードがある
  • Large BGP Community 対応バージョン
⬇️対象 \ 表現➡️ bbb:xxx bbb:xxx.*
bbb:xxx ⭕️ する ⭕️ する
abbb:xxx ❌ しない ⭕️ する
target:abbb:xxx ❌ しない ❌ しない
target:aaabbb:xxx ❌ しない ❌ しない
bbb:xxxy ❌ しない ⭕️ する
aaa:bbb:xxx ❌ しない ❌ しない
  • communityext-communitylarge-community キーワードで分離.異なるものにマッチしない
  • \d+:\d+ パターンであれば^\d+:\d+$ と解釈.それ以外はそのまま

Quagga

  • 正規表現(横) がマッチ対象(縦) にマッチするか
  • アルファベットはBGP Community を10進で表現したときの1ケタ.4ケタ = 2Bytes,6ケタ = 4Bytes
  • community-list の例.ほかにextcommunity-listlarge-community-list キーワードがある
  • Large BGP Community 対応バージョン
⬇️対象 \ 表現➡️ bbb:xxx bbb:xxx.*
bbb:xxx ⭕️ する ⭕️ する
abbb:xxx ⭕️ する ⭕️ する
target:abbb:xxx ❌ しない ❌ しない
target:aaabbb:xxx ❌ しない ❌ しない
bbb:xxxy ⭕️ する ⭕️ する
aaa:bbb:xxx ❌ しない ❌ しない
  • community-listextcommunity-listlarge-community-list キーワードで分離.異なるものにマッチしない
  • そのまま正規表現マッチ

Large BGP Community に対する懸念

2バイトの5ケタAS事業者が^ccccc:xxx$ と書くべきところを「ふつう2Bytes:2Bytes だし」とサボってccccc:xxx$ と書いてしまった場合に,意図しないLarge BGP Community にマッチして誤動作しないかを気にしていた.aaa:ccccc:xxxaaa:bccccc:xxx にマッチするかもなと.

4ケタ以下事業者は^ 忘れてないだろうし,4バイト事業者はうまくBGP Community を使えていないのでは と思われるので,問題になるとすれば2バイト5ケタかなと.

上記のようにまとめてみると,各実装とも既存の2Bytes:2Bytes Community と4Bytes:4Bytes:4Bytes のLarge BGP Community をキーワードで分離させる方針のようなので,杞憂かもしれない.

(JUNOS でExtended Community にマッチしてしまう細かな問題はあるかもしれない)

JUNOS のL キーワードはこれでいいんだっけ?

「4バイトの場合はL キーワードを使え」とドキュメントに書いているが,4Bytes:2Bytes もしくは 2Bytes:4Bytes だけであればこれでよかった.ところが4Bytes:4Bytes:4Bytes になると破綻するように思う.

aaa:bbb:ccc のうち,aaa はAS番号固定かもしれないが,bbb, ccc は2バイトレンジかもしれないし,4バイトレンジかもしれない.L の有無について4パターン併記しないといけない? それはつらい.

どういう実装で出してくるか楽しみだが,「L キーワードは捨てて,あらゆるCommunity について文頭~文末まで正規表現で文字列マッチします」というのがかえって分かりやすい気がする.

*1:手元に古いのしかなかった 😭

ステルスWiFi AP を駆逐した

macos がステルスWiFi AP に接続したときに脅してくるこれ,しばらく意味が分からなくて放置していた.

たぶんこういうことかな,というのを教えてもらって「なるほど,良くないかもしれん」と思い駆逐した.

ステルスAP が接続リストにいるリスク

アクティブにProbe Request を投げるため,接続可能なSSID の一部が漏れるから.せっかくMac Randomization してもSSID がリストで漏れており,個体識別されるリスクが高まる.ステルスじゃなくしたら漏れないかというと そうではないので「漏れやすさ」の問題.

ステルスAP が設定されているmacos 10.12.2 とIOS 10.2 の動き

ちゃんと調べたわけではありません.もし違っていたらご指摘ください.あれこれ操作したときの振る舞いを1時間くらい観察しました.

  • ステルスなSSID を探して,すべてのSSID についてProbe Request を投げまくる
  • ステルスAP に接続中のときも,そのSSID を含めて投げまくる

  • 違うAP がそのSSID を偽装したら

    • 正規のステルスAP がいない環境で,偽のステルスAP (SSID 同じ,認証なし) をつくる
    • 自動でそいつに接続することはなかった

ステルスAP が設定されていないmacos 10.12.2 とIOS 10.2 の動き

  • WiFi をON にした瞬間はActive Scan する = Probe Request を投げる
  • 放っておくとIOS 10.2 はActive Scan する
    • 端末がSleep しているのと関係あるかも (くわしく調べてない)
    • macos はそんな動きをしない (こちらはSleep していない)
  • それ以外SSID を投げていない.

かなりProbe Request が減った.

ステルスをやめる

そもそもステルスにしていたのは視界に入れたくなかったから.ほとんどメンテ不要なやつは消えていてほしい.とはいうものの,どこに持ち込むかわからない端末のほうのセキュリティリスクを下げたい.ステルスをやめることでAP のセキュリティリスクはさほど上がらない.

a/b/g/n/ac など広く802.11 を受けられる端末で

sudo tcpdump -I -ien0 -e -s0 type mgt subtype probe-req

を実行しつつ,一台ずつWiFI off/on してヘンなSSID を投げなくなるまで設定を削除した.

IOS はAP が存在しないと消せないので面倒だが,めちゃくちゃ古いSSID を探していて「おお…こいつ…こんなになっても〇〇を覚えてるのか…」って感傷的になれるのでオススメです.

BGP Community を透過する事業者がどれくらいいるか調べた

11月末にInternet Week 2016 というイベントで,「BGP Community の基本設計」について発表してきた.内容については 発表スライド をご覧いただければ良いかな と思うが,壇上から挙手アンケートを取ってみてびっくりした.

「顧客から受信したBGP Community を消さずに透過しているかた,どのくらいいますか?」の結果が 1%ほどだった.

該当するけど手を上げられなかった方もいると思うし,母数として全員がAS 運用者じゃなかったかもしれない.が,「えっ! 少なすぎる!」と思った.世の中そんなもんなのかな,と思ってざっと調査した内容を書いておく.

結果から書いておくと「割合的にはまあそんなもん」だった.

BGP Community は API なんですよ

BGP Community は「便利なタグ」だったり「他社ネットワーク内での自社経路のふるまいを定義できるノブ」だったりする.たとえば

  • 経路がネットワークに入ってきたのはどの国か,顧客に伝えるタグ
  • 他社ネットワーク内でLocal Preference を下げるためのノブ

のように,BGP プロトコルの範囲のなかで付加価値をつけることができる.「HTTP(S) でやれば?」という考えも頭をよぎるが,IP レイヤーの操作をさらに上のレイヤーに依存するのは危険かもしれないし,レイヤーをまたぎすぎている感がある.「HTTP(S) でもできる」が理想だけれど,IP ならではの柔軟性と拡張性を得るためにもBGP プロトコルでやりたいところ.

上のように AS65000 がBGP Community に乗せて便利情報を提供してくれている場合,AS65001 の立場からすると「自社に不利益がある」「AS65000 の利用規約に反する」ような場合を除いてAS65002 に透過しない理由はないはず.逆もしかりで,AS65002 が「AS65000 内での経路のふるまいを制御したい」と考えているとしてAS65001 がそれを遮断する理由はないように思う.

が,アンケート結果では「でも止めてる」だし,観測範囲内でも止めてる事業者がほとんどに見える.

特にAS65002 がAS65001 の顧客だった場合,情報を透過するほうがAS65001 サービスの付加価値につながりそうだし,ぜんぶ透過しなくても工夫してやればいいのに…と思う.

「将来,トランジット事業者(上の絵でいえばAS65000) を変えるかもしれない」という懸念はあるが,トランジット事業者が似たAPI を提供してさえすればAS65001 で変換するのがよさそう.他社のサービスや製品を組み合わせてうまいこと抽象化することは,サービスを作る側の手腕の見せ所かな と思う.

調査結果

AS間の関係を transit / peer / customer / unknown に分類し「X から受信したBGP Community をY に透過する事業者数」を数えた.調査の方法はあとで.

たとえば transit からcustomer に向かう矢印は93 だが,「transit から受信したBGP Community をcustomer に透過するAS は93 個あった」という意味.4バイトAS は除外してカウントしている.

BGPlay コレクターの配置的にすべてのAS をカバーしていないかもしれないが,フルルートの1/11 ほどのサンプル数.

  • customer から受信した経路を透過する事業者が比較的多そう
    • 送信先によって透過 / 不透過を区別してそう
    • 特にtransit 方向にはフィルターしてそう
  • 実際に使われている2バイトAS 約6万個に対して,透過しているAS はおよそ1% 程度しかない

ということがわかる.unknown が多いのは,参照したAS間の関係データベースが良くなかったかもしれない.

調査方法

BGPlayThe CAIDA UCSD as-relationships - 20161201 のデータを使った.

2バイトAS のすべてについて

  1. そのAS がoriginate している適当な1 prefix を選ぶ
  2. BGPlay でBGP Community が透過しているかどうか調べ,AS_PATH 上の各AS について transit / peer / customer のどれからどれに送信する際に透過するかをマークする
  3. AS 間の関係についてはCAIDA のas-relationships を参照する

仮定として

  • BGPlay コレクターはcustomer 相当と考える
  • BGP Community のGlobal Administrator がAS_PATH 上に現れるもののみをカウント
    • 現れない場合,BGP Community がどこでつけられたか判別できない
    • そのBGP Community は,AS_PATH 上のGlobal Administrator より右でつけられたものと考える

たとえば

  • AS_PATH: 65000 65001 65002 65003
  • BGP Community: 65002:100

の場合

  • Community は65002 か65003 でつけられたと仮定
    • → 65001 はCommunity を透過する
  • 65001 から見て65002 は transit で,65000 は customer
    • → 65001 は transit から受信したBGP Community を customer に透過する
  • 65000 から見て65001 は customer で,BGPlay コレクターも customer
    • → 65000 は customer から受信したBGP Community をcustomer に透過する

経路数でいえばフルルート68万経路に対して6万経路分,1/11 サンプルを調査した.BGPlay コレクターの配置から「透過しているんだけど観測できない」AS があることにも注意.

なお,今回は割合を概算したかったので 4バイトAS は除外してカウントしている.

Internet Week 2016 発表スライド

発表スライドをリンクしておきます.

  • AS 運用者向け
  • BGP Community 設計のコツ
  • API と思って設計しよう

のような内容です.よければご覧ください.

https://speakerdeck.com/codeout/bgp-communityfalse-ji-ben-she-ji

xlogin でコマンド自動投入したあと,手動制御にもどす

xlogin -s に渡すexpect スクリプトを書けばよい.

設定投入は自動でやって,温かみのある commit を打ちたい場合などにつかう.( そんなのcommit まで自動でやれよ,という話ではある)

jlogin の例

log_user 1

# 既存のrun_commands を参考に
proc run { prompt command } {
    # 補完を有効にしておく
    send "set cli complete-on-space on\r"
    expect -re $prompt {}

    # handle escaped ;s in commands, and ;; and ^;
    regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand
    regsub {^;} $esccommand "\u002;" command
    set sep "\\1\u001"
    regsub -all {([^\\])\;} $command "$sep" esccommand
    set sep "\u001"
    set commands [split $esccommand $sep]
    set num_commands [llength $commands]
    for {set i 0} {$i < $num_commands} { incr i } {
        send "[lindex $commands $i]\r"
        expect {
            -re "^\[^\n\r *]*$prompt $" {}
            -re "^\[^\n\r]*$prompt." { exp_continue }
            -re "(\r\n|\n)" { exp_continue }
        }
    }
}


# 自動で入力したいコマンド群
set cmd_text "configure
set interfaces ge-0/0/0 description foo
show | compare
commit check
"

set command [join [split $cmd_text \n] \;]
run $prompt $command
interact

コマンド群を自動で send したあと,interact を呼べばいい.

実行例

configure という名前で保存してあるとして,

codeout $ jlogin -s configure router1
router1
set cli complete-on-space on
Enabling complete-on-space

codeout> configure
Entering configuration mode
The configuration has been changed but not committed

[edit]
codeout# set interfaces ge-0/0/0 description foo

[edit]
codeout# show | compare
[edit interfaces ge-0/0/0]
+   description foo;

[edit]
codeout# commit check
configuration check succeeds

[edit]
codeout#

[edit]
codeout# <-- ここで手動制御にもどる

複数台に対して同じことをやりたい場合,

codeout $ jlogin -s configure router1 router2 ...

のようにまとめて呼ぶ.

ほかのclogin などで試してないが,同じような処理でいけるはず.

MRAI とBGP Withdrawn とパケットロス

BGP には,無駄な経路計算を減らすためのMRAI Minimum Route Avertisement Interval) というしくみがあります.BGP Update を送信する前に一定時間待ち,経路変化をバッファリングできる機能です.バッファ中にさらに経路変化があった場合,BGP Update を二度送信しなくてすみます.

(MRAI の概要については前のエントリーをご覧ください)

codeout.hatenablog.com

一方で,MRAI の実装によりリスクも存在します.経路変化を下手にバッファすることで,たとえばルーティングループが発生し,パケットロスする可能性があります.本エントリーでは どのような場合にパケットロスが発生しうるか,さらにそれを緩和するために何ができるかについて考えてみます.

十分に検討できてないかもですし,いろいろご意見いただるとうれしいです.

目次

経路を加えるBGP Update と経路を消すBGP Update

タイトルには便宜的にBGP Withdrawn と書いてしまいましたが,「経路を消すためのBGP Update メッセージ」の意味で使っています.経路を加える場合も消す場合もBGP Update メッセージをつかいますが,ここでは次のように呼び分けることにします.

  • BGP Update
    • 経路の増加や変更を伝えるためにつかう.NLRI フィールドやPath Attributes フィールドをつけて送る
  • BGP Withdrawn
    • 経路の削除を伝えるためにつかう.Withdrawn Routes Length フィールドやWithdrawn Routes フィールドをつけて送る

対称的な2 つのUpdate メッセージですが,BGP Withdrawn については注意して送信しないとマズい場合があります.

BGP Withdrawn に注目するのはなぜか

BGP Withdrawn はループの原因になりえるから

BGP での経路コンバージェンスを考えるとき,BGP Withdrawn はBGP Update より弱い側面があります.BGP のしくみ上,BGP Withdrawn をトリガーにルーティングループが起こる可能性があるからです.

なるべくシンプルな例で説明しますが,たとえば 4x ルーターが次のように接続されていて BGP ピアが張られているとします.

初期状態:

  • 右のほうで10.0.0.0/20 がオリジネートされていて
  • 左のほうで10.0.0.0/24 がオリジネートされている

10.0.0.0/24(左) の経路が消えるとどうなるか.

  1. 左からBGP Withdrawn が伝搬し,10.0.0.1 のNextHop が右向きに切り替わる (page 3)
  2. さらにBGP Withdrawn が伝搬し,次のルーターでもNextHop 右向きに切り替わる (page 4)

page 3 でNextHop が互いに向かい合っている = パケットはループ(ピンポン) する ことに注意してください.BGP のしくみ上これを避けることが難しいため,なるべくこの期間を短くして

  • 一旦パケットロスするが,TCP で再送されるまでにループが解消されている
  • あるいは IP TTL が0 になるまでにループが解消されている

ようにすることが理想的です.

一方 BGP Update の場合はこのようなループが発生する可能性が少ないと考えられます.

初期状態:

  • 右のほうで10.0.0.0/20 がオリジネートされている

10.0.0.0/24(左) の経路が出現するとどうなるか.

  1. 左からBGP Update が伝搬し,10.0.0.1 のNextHop が左向きに切り替わる (page 7)
  2. さらにBGP Update が伝搬し,次のルーターでもNextHop 左向きに切り替わる (page 8)

どの状態においてもBGP Withdrawn の時のようなループは発生しません.Best Path (パケット転送に使われる最も強い経路) だけを伝搬させるBGP のしくみ上 このような動きになります.

もちろん,さらに複雑な構成でBGP Update をトリガーとしてループが起こることはありますが,Indirect Nexthop がループしていたり,経路フィルターなど他の要因とのコンボであることがほとんどです.

BGP Withdrawn は伝搬に時間がかかるから

BGP Withdrawn を送信する際には「過渡期 = BGP コンバージェンスタイム を短くする必要がある」という話をしました.しかしながら,BGP Update と比較してかなり長いことが分かっています.

一例として,JANOG38 で@maz_zzz さんがおもしろいデータを紹介していました.

f:id:codeout:20160810014452p:plain
色々なトラヒック制御の利点と欠点

これはRIPE のRIS Routing Beacon を日本のISP で観測したデータですが,BGP Update(左) の伝搬時間に対し,BGP Withdrawn(右) はおよそ3 倍も長くなっています.

BGP 運用者にとって,これは感覚的に納得できるデータです.というのは「BGP Withdrawn ってそんなもん」だからですね.*1

シンプルな例で説明してみます.

  • AS65000~AS65005 が上のようにBGP ピアを張っている
  • 各AS の関係は ピア / トランジットが入り混じっている
  • AS65004(左下) の経路がAS65003(右中) でどのように見えるかを考える

AS = 通信事業者と考えてください.AS 間の関係については@yuyarin さんのスライド(page 21~31 あたり) が分かりやすいです.

AS65004(左下) が持つ経路は,矢印の流れに沿ったBGP Update によって伝搬し,AS65003(右中) では3 Path から聞こえてきます.BGP では複数Path の経路をまとめて1 つとして扱うため,3 Path 中1 つでもBGP Update を受信すれば「伝搬した」と考えることができます.

一方 BGP Withdrawn の場合も同じ矢印の流れに沿って伝搬しますが,

「経路が消える」とは3 Path すべてが消えることを意味するため,BGP Withdrawn の伝搬時間は「最も伝搬時間の遅いPath」に律速されてしまいます.

こうしてみると,BGP Update に比べてWithdrawn の伝搬に時間がかかることが原理的に理解できると思います.

メモ: BGP Update のコンバージェンスタイム

一般に BGP コンバージェンスタイム は「定常状態に落ち着くまでの時間」を指すので,上記のような最短の1 Path のBGP Update 伝搬時間とは異なります.しかしながら最強のPath が最速で伝搬することが少なくないため,やはりBGP コンバージェンスタイムもBGP Withdrawn >> BGP Update になりがちです.

MRAI + BGP Withdrawn の危険性

さて,すでに長くなりましたがBGP Withdrawn に注目する理由について説明しました.

  1. BGP Withdrawn トリガーでルーティングループが発生することを,原理上避けられない場合がある
  2. 時間にして100s のオーダーになる可能性がある

という2点です.特に2 . について,MRAI は不安定な時間が長くなる原因のひとつなのでは? と考えています.

前のエントリー で調査した Juniper vSRX / Cisco IOS-XRv / Quagga の例でいえばvSRX パターンのMRAI 実装(out-delay) がまずいです.BGP Withdrawn とのコンボにより「RIB からすでに消えているのに,N 秒間 (= MRAI タイマー値) はBGP Withdrawn を出さない」という動きをするからです.ちょうど「N 秒間は経路オリジネート = discard し続ける」ような動作です.

BGP Neighbor ルーターではRIB から消えず,上のようなルーティングループ状態を維持してしまう.運悪くMRAI ルーターが直列する場合,N 秒は積算されます.実際のインターネットでは 5.5万AS が網状に接続されており,周辺パケットを吸い込んで捨てるブラックホールが点々と動き回るように見えるはずです.

実際のインターネットで起こりうるか

単純なモデルではMRAI + BGP Withdrawn がパケットロスの原因になりそうですが,実際のインターネットで起こりうるかについて,いくつかの例で考えてみましょう.

未使用の/24 を一時的に広告し BGPlay などで観測すると良いかもしれません.なにが起こるか推測しやすくなります.

例1: DDoS 対策サービスを使っている

BGP でトラフィックを引き込むタイプのDDoS 対策サービスを使っている場合.

DDoS トラフィックを一旦べつのAS に引き込んでおいて,DDoS だけをフィルターしてオリジンAS に戻します.引き込む際にはBGP で細かい経路が広告されますが,IPv4 の慣例的では インターネットに伝搬する最小サイズ = /24 であるため,DDoS 被害ホストを含む/24 を広告することが多いです.

さて,DDoS 自体が止まって トラフィックを引き込んでいた/24 広告を止めるとどうなるか.

実線の矢印 → (青/赤) はNextHop = パケット転送の向きと思ってください.さらに「どの経路をもとにパケット転送するか」を矢印の色で表現します.矢印(青) は10.0.0.0/24 がBest Path であり,その方向にパケット転送することを示します.

仮にAS65000 にout-delay 的MRAI が設定されているとすると

  1. AS65001(DDoS 対策サービスプロバイダー) からBGP Withdrawn が伝搬し,AS65000 RIB から/24 が消える → NextHop が下向きに切り替わる (page 11)
  2. ループした状態で N 秒(= MRAI タイマー値) 維持 (page 12)

例2: 経路広告によるトラフィックエンジニアリング

2事業者からトランジットを買っているAS65004 が,トラフィックエンジニアリングのため細かい経路を片方に広告する場合.

重要経路を片寄せしたいとか,エンドユーザーの意向でAS65003 には広告したくない などの想定.仮にAS65001 にout-delay 的MRAI が設定されているとします.

ここから,/24 の経路広告が止まるとどうなるか.

  1. AS65004からBGP Withdrawn が伝搬し,AS65002 RIB から/24 が消える → が,NextHop は変化なし (page 22)
  2. さらにBGP Withdrawn が伝搬し,AS65003 のみNexthop が上向きに切り替わる (page 23)
  3. さらにBGP Withdrawn が伝搬し,AS65001 でNexthop が下向きに切り替わる (page 24)
  4. ループした状態で N 秒(= MRAI タイマー値) 維持 (page 25)

AS65002-AS65004 間リンクが切れた場合でも似たような動きになるはずです.

でも,BGP Withdrawn 自体レアなのでは?

はい.正常時あまりBGP Withdrawn を送ることがないかもしれません.日本で観測されるBGP メッセージのうち,Withdrawn は10% に満たない程度です.短期間で経路広告/停止しない場合はさほど気にする必要ないはずです.

いっぽう短期間に経路広告/停止する場合,

  • 絶妙な位置にMRAI ルーターがあると,最大数分間通信が止まる
  • 遠くにあるMRAI ルーターでも,その周辺の通信を止めている (遠いので影響が見えにくいだけ)

という可能性を念頭に置いておくと良いでしょう.

パケットロスを緩和するために

例1: BGP Withdrawn の影響をあらかじめキャンセルする

/24 からトラフィックを抜くのは厳しいので,基本的にはルーティングループを発生させずにBGP Withdrawn を伝搬させる戦略になります.

場合により有効な手段があります.たとえば例1: DDoS 対策サービスを使っている ケースでは

  1. トラフィックを引き込んでいる/24 より強い/24 をオリジンAS から広告する (page 15)
  2. DDoS 対策サービスを止める → 経路を上書きしているためループは発生しない (page 16)
  3. 上書きしていた/24 も止める → オリジンが同じためループは発生しない (page 17~18)

のように,BGP Withdrawn の悪影響をあらかじめキャンセルすることができます.ただし,DDoS 対策サービスが,トラフィックを引き込む/24 をあらかじめ弱めに広告できる ことが前提です.

例2: BGP Withdrawn の影響を局所化する

「このあたりでいつもループする」という事実を把握していない限り,多くの場合は対策が困難です.しかしながらBGP Withdrawn がルーティングループを生んでいると考えられるなら,それを局所化することは可能です.

Withdraw するべき経路のBGP Update を,あらかじめ日本国内 / アジア圏内 / 特定AS内に制限しておけばよい.たとえばオリジンAS が海外拠点を持たない場合は,DDoS 対策やトラフィックエンジニアリング向け経路は日本だけで十分です.

ただし,経路の伝搬範囲を制御するオプションを,トランジットISP が提供している ことが前提です.多くないと思いますが,そのようなISP は国内にもいます.今後増えていくことを期待しています.

まとめ

いくつかのMRAI 実装のうち,JUNOS のout-delay 的な実装とBGP Withdrawn の相性が悪く ルーティングループを生む場合があることを説明しました.経験的には,このようなループは30秒~数分間の通信断の原因になることがあります.

トランジットISP がMRAI を設定する際には 思わぬ悪影響がないかを検討してもらいたいですし,ユーザー側としては「MRAI ルーターがインターネット上にある」前提に立って,ルーティングループを検知し,キャンセルする策について検討しておくとよいでしょう.

*1:AS接続構成に依存するので常に3 倍とは限らないが,だいたい数倍遅い

MRAI の代表的なふるまい

BGP には古くからMRAI (Minimum Route Advertisement Interval) という機能がある.

イマイチ理解していなかったが,動作を確認しておく必要があり いくつかの実装を調査した.

ざっくり言うと

vSRX IOS-XRv Quagga
新しいprefix の追加時 MRAI 待つ MRAI 待つ(*2) MRAI 待たない
最後の経路削除時(*1) MRAI 待つ MRAI 待たない MRAI 待たない
経路切替時 Best Path になってからMRAI 待つ 前回のUpdate からMRAI 待つ MRAI 周期でのみUpdate
  • (*1) prefix ごとの,最後の経路(path)
  • (*2) 5% 程度,待たないpacket があったため要調査

このようにさまざまな実装がある.

以下目次:

MRAI (Minimum Route Advertisement Interval) とは

簡単にいえば「BGP Update Message の送る頻度を,prefix ごとに制限する」機能のこと.多くのルーターではBGP neighbor 単位で設定できる.

インターネットでは,各事業者のネットワーク拡張や障害などによるBGP 経路の変化が日常的に起こっているが,短時間に大量に変化したり,長時間経路がフラップし続けるとヤバい場合がある.たとえば,末端のちいさなルーターで経路計算が追いつかず,通信に影響が出たりする.

BGP は受け取った経路をもとに各ルーターがベストパスを計算し,ピアに伝達する (BGP Update を送る) プロトコルのため,ピアから大量のBGP Update を受け取った場合は力の限り計算し,力の限り 別ピアに送りつける.

MRAI はこの動作を緩和するしくみで,力の限り計算するが,結果を送りつけるときには 一定期間の間をあけて やさしく送ることができる.

多くの実装で「期間」を設定することができるが,「何と何のあいだの期間か」は実装による. だいたい共通するのは,

  • BGP neighbor 単位で設定できる
  • 単位は秒
  • ピア & prefix (BGP 経路の宛先情報) のペアごとにタイマーを持つ

おおまかには,MRAI は経路計算結果をバッファしておいて 一定時間待ってフラッシュするしくみと捉えておけばいいと思う.同じprefix について何度も経路計算したとしてもフラッシュ時には1 Update になるため,結果的にBGP Update の数を抑えることができる.

MRAI にはデメリットもある点に注意.バッファすることでBGP Convergence が遅くなる場合もあり,インターネットを少しずつ不安定にしてしまうかもしれない.これについてはまた別のエントリーで書く.

MRAI の標準化

この機能は10年以上も前から実装があるが,2008 年にInternet Draft が書かれている.最新のI-D もExpire していて 現在でも標準はない.各社独自実装している状況.

最新のI-D によると

The Minimum Route Advertisement Interval (MRAI) timer is specified in RFC4271 [BGP]. This timer acts to rate-limit updates, on a per- destination basis. [BGP] suggests values of 30s and 5s for this interval for eBGP and iBGP respectively. The MRAI must also be applied to withdrawals according to RFC4271, a change from the earlier RFC1771.

Withdrawn にも適用せよ,とのこと.

MRAI の動作

  • Juniper vSRX 15.1X49
  • Cisco IOS-XRv 5.2.2
  • quagga 0.99.23

のふるまいを調べてみた.各々設定は

Juniper vSRX
protocols bgp {
  group foo {
    out-delay 30;
  }
}
  • default: 0s
Cisco IOS-XRv
router bgp 65000
 neighbor 192.168.0.75
  advertisement-interval 30
  • default: 0s (iBGP), 30s (eBGP)
Quagga
router bgp 65000
 neighbor 192.168.0.75 advertisement-interval 30
  • default: 5s (iBGP), 30s (eBGP)

こんな感じ.

経路の追加 / 削除時

まず,ベストパス選択がない場合の動作.RIB にない経路を入れ,消す.

左から重複のない新たな経路20000 prefix をBGP Update として入れ,経路処理して右に送り出すまでの時間を見る.十分時間経過したあとにBGP Update (Withdraw) を入れつつ同様に観測する.

経路の追加: Juniper vSRX (MRAI なし)

横軸に時刻(s),BGP Update Message (パケット) を受信したタイミングを青レーンに描画,BGP Update Message (パケット) を送信したタイミングを黒レーンに描画した.

Best Path Selection がないとはいえ結構な遅延があるが,Update 受信 → 経路処理 → Update 送信 までInterval をおかず処理していることがわかる.

経路の追加: Juniper vSRX (MRAI: 30s)

グラフのスケールが違ってて申し訳ないが,経路処理の遅延に加えて30s のInterval をおいていることがわかる.

経路の削除: Juniper vSRX (MRAI なし)

追加と同様に,MRAI がなければInterval なし.

経路の削除: Juniper vSRX (MRAI: 30s)

追加と同様に,Withdraw 時にも30s のInterval が乗る.

経路の追加: Cisco IOS-XRv (MRAI なし)

次にIOS-XRv の結果.Interval は見られない.

Juniper xSRX に比べてBGP Update 処理遅延が小さいことも興味深い.
(同じような処理プロセスだが爆速なのか,並列化など手続き自体を工夫しているのか調べてない)

経路の追加: Cisco IOS-XRv (MRAI: 30s)

Interval を待たず 何か一部お漏らししているように見えるが,(青い四角の下の黒い線) たぶん30s 待ちたいんだろうなあと汲み取った.Interval なくUpdate を送ってしまっているのは,全体300 packet 中の20 packet.

経路の削除: Cisco IOS-XRv (MRAI なし)

Interval は見られない.

経路の削除: Cisco IOS-XRv (MRAI: 30s)

興味深いのは,Interval が見られない点.

送信したBGP Update (黒い線全体) は60 packet ほど.経路追加時には20 packet ほどInterval を待たないBGP Update があったが,必要になればあとで詳しく調べる予定.

経路の追加: Quagga (MRAI: 30s)

次にQuagga のふるまい.一見Interval をおいているように見えるが,よくみると4s に満たない程度なので,これはBGP Update 処理遅延と考えるのが妥当.

経路の削除: Quagga (MRAI: 30s)

Cisco IOS-XRv のようなふるまいで,Interval がない.

ここまでのまとめ: MRAI の動作 (経路の追加 / 削除時)

長くなったので一旦まとめ.

vSRX IOS-XRv Quagga
新しいprefix の追加時 MRAI 待つ MRAI 待つ(*2) MRAI 待たない
最後の経路削除時(*1) MRAI 待つ MRAI 待たない MRAI 待たない
  • (*1) prefix ごとの,最後の経路(path)
  • (*2) 5% 程度待たないpacket があったため要調査

MRAI の動作 (続き)

経路切替時

次にBest Path Selection が走る場合.

同じAS と2 ピア張り,連続的にBest Path Selection が実行されるよう強弱 *1 をつけて同じprefix のBGP Update を送る.

経路の切替: Juniper vSRX (MRAI: 30s)

下のグラフは,

  • 横軸: 時刻(s)
  • 左からBGP Update を受信した時刻 = 正の値をプロット
  • 右へBGP Update を送信した時刻 = 負の値をプロット
  • 線が長い = 強い経路
  • 線の色が同じ = 同じ経路 *2

のように描画したもの.

MRAI: 30s に対して10s ごとに強弱のBGP Update を入れた場合:

40s ごとに強弱のBGP Update を入れた場合:

Juniper vSRX は

  • 送信するべき経路がBest Path になってから,30s (MRAI) 経過して初めてBGP Update を送信
  • 送信するべき経路のBest Path が<30s の間に変わり続ける場合,BGP Update は送信されない

のようにふるまうことがわかる.

経路の切替: Cisco IOS-XRv (MRAI: 30s)

MRAI: 30s に対して10s ごとに強弱のBGP Update を入れた場合:


(60s すぎのBGP Update 受信(青) と送信(オレンジ) が重複している部分,送信のほうが若干早い)

40s ごとに強弱のBGP Update を入れた場合:

Cisco IOS-XRv は

  • 前回BGP Update を送信してから,30s (MRAI) を経過して初めてBGP Update を送信
  • 30s 待つ間にBest Path が変わっていれば30s ですぐ送信するし,変わっていなければ以降Best Path が変わるまで送信しない
    • 変わった時点ですぐ送信

のようにふるまうことがわかる.

経路の切替: Quagga (MRAI: 30s)

MRAI: 30s に対して10s ごとに強弱のBGP Update を入れた場合:

40s ごとに強弱のBGP Update を入れた場合:

Quagga は

  • 30s (MRAI) ごとのサイクルでのみBGP Update を送信する
  • 30s おきにBest Path が変わっていれば送信するし,変わってなければ送信しない

のようにふるまうことがわかる.

ここまでの3実装のふるまい(経路切替時)について,あるprefix に限定した動作であることに注意.prefix が異なれば送信タイミングは独立している.

2016/07/23 追記

コード読んだところQuagga はprefix ごとのタイマーを持たない.BGP neighbor 単位.prefix が違っても 一定のサイクルでまとめてUpdate 送信する.

まとめ

全体のまとめ.

vSRX IOS-XRv Quagga
新しいprefix の追加時 MRAI 待つ MRAI 待つ(*2) MRAI 待たない
最後の経路削除時(*1) MRAI 待つ MRAI 待たない MRAI 待たない
経路切替時 Best Path になってからMRAI 待つ 前回のUpdate からMRAI 待つ MRAI 周期でのみUpdate
  • (*1) prefix ごとの,最後の経路(path)
  • (*2) 5% 程度,待たないpacket があったため要調査

見てわかるように,実装によりふるまいがけっこう違う.

目的によってMRAI がハマるとき / ハマらないときがある気がするけど,「このベンダーのこのバージョンでは…」といった具体的な仕様はさておき,多種多様な実装があることを知っておけばひとまず十分.MRAI を検討するときに

  • 守りたいものはなにか
  • MRAI で自衛できるものか
  • 使っているルーターのMRAI はどのように動くか.守りたいものを守れるか
  • MRAI を入れる → Convergence Time が伸びて困ることはないか
  • 周辺のAS はどのようにふるまうか

などを考えればよいかな,と思う.あとデフォルトが0s でない装置もあるので知らず知らずのうちに使っているかもしれない.

*1:強弱はMED で制御した

*2:BGP Community でマークした