LGTM

Looks Good To Me

退職時の年金戦略

昨年12月に会社勤めをやめ,個人事業主になった.5ヶ月たって やっと退職関連の手続きが終わったので,なんでこんな面倒なことになったのかメモっておく.

はー長かった…

企業年金の移換に時間がかかった

会社員だと 企業年金厚生年金基金の積立をしている場合がある.これを個人型の確定拠出年金(DC) に移換しようとしていたため,ものすごい時間かかった.

なんでそんなことしたの?

雑にいえば,税金を取られたくなかったから.

退職時点で 規約型企業年金と企業型確定拠出年金の積立があった.勤務中に年金制度が変わったためにいろんな年金が混ざっているが,

  • 企業年金 → 退職一時金として全額もらうか,個人型DC に移換できる
  • 企業型DC → 個人型DC に移換

前者には選択肢があるが,会社の担当者いわく「みなさん,ほとんど一時金を選択します」とのこと.それ終身雇用の名残なのでは… よく考えないと 自分にあってるかわからないよ? 損するかもよ?

退職一時金を選んだ場合

国民年金と厚生年金の上の3階部分なので 額は大きくないかもしれないが,それでも勤続年数が長いと100万のケタに乗る場合がある.それを受け取ってなにかの資金にするのもアリだが,問題はガッツリ税金をとられること.

退職一時金は退職所得なので,退職金と合算の上 源泉徴収される.所得が増えるので,翌年の住民税が高くなる.

自分の場合は160万の原資に対して,源泉徴収分だけで10% ほど持っていかれる計算だった.

個人型DC に移換する場合

現金が少なくなるのはイタいが,積み立てた原資をそのまま移せる.所得にならないので 翌年の住民税を抑えられる.

自分の場合は150万の使いみちが決まってなかったし,収入が不安定になるから老後とかめちゃくちゃ心配だし,年金運用の資金を少しでも増やすためにこっちを選んだ.

税金って言っても,たかだか15万円とかでしょ?

個人型DC は課税を繰り延べできるので 複利で効いてくる.仮に年5% で運用できたら,30年後には

  • 原資 145万円 → 145 x (1.05 ^ 30) = 626万円
  • 原資 160万円 → 160 x (1.05 ^ 30) = 691万円

65万円のちがいになってる計算.目減りさせないことが鉄則なのです.

繰り延べと言っても,いまの制度だと公的年金と同じ控除 が受けられるので 受給時には税金がかからない可能性もある.

もちろん65歳までに死ぬかもしれないし,運用成績がマイナスになる可能性もあるが,まあそこは目をつぶろう.

まとめ

会社勤めからフリーランスになった場合,もしすぐ現金を必要としてなかったら,企業年金とか厚生年金基金は個人型確定拠出年金に移換したほうが有利そう.

ちなみに,よく知らないが転職の場合も同じように選択できるはず.

BGP Flowspec のバリデーション

BGP ルーターには,Flowspec という機能があります.2009年にRFC5575 として標準化されたもので,ざっくり言えば「遠くのルーターでパケットフィルターを発動させる」機能.

最近 注目されているこのFlowspec のうち,とくにバリデーションについて調べました.

2015-05-28 追記

@shtsuchi さんに指摘をもらい,IOS-XRv の実装が分かりましたので 追記しました.Flowspec ちゃんと動きます.コメントありがとうございました!

2023-01-31 追記

@a16tochjp さんに JUNOS 実装についてコメントをもらい,RFC9117 について追記しました.ありがとうございました!

ところで,使われてるの?

当初脚光を浴びましたが 大規模にデプロイする事業者がなく,その後しばらく下火でした.ここ数年でコンテンツプロバイダー中心にアーリーアダプターが使いはじめ,少しずつ注目されてきたのが現状です.

https://www.slideshare.net/Arbor_Networks/aol-flowspec-2015

BGP Flowspec とは

JANOG35 のセッション が詳しいです.

簡単に言えば

  1. トラフィックを特定するためのL3/L4 情報
  2. 特定したトラフィックの処理方法

をBGP に乗せて伝搬させます.

  1. 通常のBGP で言えば「10.0.0.0/8」が入っていたところに「dst prefix=172.16.0.0/16, src prefix=any, proto=1」のような情報が
  2. BGP community のところに「rate-limit させる」のような情報が

入っているイメージです.*1

show route すれば「ああ,素直に出力してるんだな」と分かる程度にそのまま表示されます.

バリデーション?

Flowspec は パケット操作方法をeBGP にも乗せられる強力な機能なので,標準にも厳しくバリデーション方法が指定されています.各ルーターでは,Flowspec 経路それぞれをバリデーションし,valid であればそこに記述されているパケットフィルターを発動させます.

A flow specification NLRI must be validated such that it is considered feasible if and only if:

a) The originator of the flow specification matches the originator of the best-match unicast route for the destination prefix embedded in the flow specification.

b) There are no more specific unicast routes, when compared with the flow destination prefix, that have been received from a different neighboring AS than the best-match unicast route, which has been determined in step a).

By originator of a BGP route, we mean either the BGP originator path attribute, as used by route reflection, or the transport address of the BGP peer, if this path attribute is not present.

けっこう重要そうな項目なのに,これだけだと意味がわかりませんね.メーカーサイトにもわかりやすいドキュメントがなかったので「いくつかの実装を試して確かめました」というのが今回の趣旨です.

試したのはJuniper vSRX (12.1X47-D15.4),Cisco IOS-XRv (5.2.2).ともにFlowspec をFIB にインストールする部分が実装されていないようで,今回はルーティングだけの確認です.

Flowspec のオリジネーターDestination Prefix

a) Flowspec のオリジネーターは,Flowspec 中のdestination prefix に対するベストマッチ経路のものと一致する必要がある

意味がわからない その1.

オリジネーターとは,BGP originator パスアトリビュートかBGP ピアのトランスポートアドレス

とあります.

BGP originator パスアトリビュートには,ORIGINATOR_ID のことですが,ルートリフレクターがクライアントのrouter ID を格納します.トランスポートアドレスは,ピアを張るIPアドレスそのものを指します.ですのでFlowspec ルーターは,RR 構成であれば経路のrouter ID を,そうでなければneighbor IP アドレスを使ってオリジネーターを識別していることになります.

なるほど.iBGP だけの世界なら単純そう.ところがiBGP とeBGP を混ぜて考えると少しわかりにくいかもしれません.

トランスポートアドレスが一致する,とは?

iBGP とeBGP がある構成を考えます.

上の構成では どこにも経路フィルターを設定していません.RTR1 から10.0.0.0/8 の経路を広告し,RTR3 まで伝えます.

この構成でRTR2 にstatic なFlowspec (dst=10.0.0.0/8) を設定したとすると,RTR3 にも伝搬しますが,そこでは該当のFlowspec 経路はvalid になります.通常の経路はRTR1 でオリジネートし,Flowspec はRTR2 でオリジネートしたにも関わらず,です.

これは経路の向きを逆にして,RTR1 上で考えたとしても同じです.

このような動作になるのは,Flowspec ルーターがトランスポートアドレスによってオリジネーターを識別しているからです.ようするに「オリジネーターが同じ」とは,「同じBGP セッションから受信した」と解釈して構いません.*2

ベストマッチ経路とは?

該当のprefix を含有する最小の経路のことです.

たとえばBGP テーブル上に10.0.0.0/8 と10.0.0.0/16 がある場合,dst=10.0.0.0/24 なFlowspec 経路に対するベストマッチ経路は10.0.0.0/16 です.

また,ベストマッチ経路が存在しない場合はFlowspec 経路はinvalid です.

逆に言うとBGP テーブル上に10.0.0.0/8 がある場合は,dst=10.0.0.0/8~/32 までのFlowspec 経路がベストマッチ経路を持ち,valid になる可能性があります.

Destination Prefix が指定されていなかった場合は?

「dst 条件がなかったらどうなんの?」と思った方はするどいですね.

  • Juniper vSRX の場合
    • dst=0.0.0.0/0 相当です.この場合のベストマッチ経路はデフォルトルート(0.0.0.0/0) です.なので,dst 条件のないFlowspec 経路をvalid にするには,デフォルトルートが必要になります.
  • Cisco IOS-XRv の場合
    • オリジネーターバリデーションしません.なので,dst 条件のないFlowspec 経路をvalid にするために,デフォルトルートは必要ありません.

ここまでのまとめ

a) Flowspec のオリジネーターは,Flowspec 中のdestination prefix に対するベストマッチ経路のものと一致する必要がある

というのは,言いかえると

Flowspec 経路と,そのdestination prefix を含む最小の経路は,同じBGP セッションから受信する必要がある

2015-05-28 追記

Cisco IOS-XRv は「iBGP からFlowspec 経路を受信した場合に限り,上のオリジネーターバリデーションをスキップする」という実装になっています.詳しくは後述しますが,Juniper vSRX と動作が違うので注意です.

More Specific な経路があってはいけない,とは?

b) 異なるAS から受信する,more specific な経路がないこと

意味がわからない その2.

こちらも経路フィルターがない構成で,RTR1 とRTR3 から経路を注入します.すると,RTR2 のBGP テーブルに10.0.0.0/8 と10.0.0.0/24 が乗ります.

この状態ではRTR2 上でdst=10.0.0.0/8 なFlowspec 経路はinvalid になり,Flowspec テーブルには乗りません.more specific な/24 の経路があるためです.

「割り当てられた/8 から/24 を他者に割り振る」ような状況も考慮すると,「/8 単位での制御は危険だからやめとくべき」という意図だと思われますが,ちょっと窮屈ですね.

また,今回のテストではなぜか「異なるAS かどうか」は評価されないように見えました.10.0.0.0/24 をiBGP から受信しようが,eBGP から受信しようが,static な経路だろうが,dst=10.0.0.0/8 なFlowspec 経路はinvalid になります.

RFC によると「異なるAS からmore specific 経路が広告された場合のみinvalid」になりそうですが,そのような動作はしません.*3

Destination Prefix が指定されていなかった場合は?
  • Juniper vSRX の場合
    • dst=0.0.0.0/0 相当なので,デフォルトルート以外の経路が存在しない場合に限り 該当のFlowspec 経路はvalid になります.通常そんなことはありえないので,destination prefix が指定されていないFlowspec は常にinvalid です.
  • Cisco IOS-XRv の場合
    • more specific バリデーションしません.destination prefix が指定されていないFlowspec もvalid になる可能性があります.

Flowspec をstatic に設定することができる

ここまでで,Flowspec のバリデーションは

  • Flowspec 経路と,そのdestination prefix を含む最小の経路は,同じBGP セッションから受信している
  • BGP テーブル上に,destination prefix よりmore specific な経路がない

ことを確認するものだ,と理解できます.

この動作に不都合を感じる場合は,Flowspec をstatic に設定することで,設定したルーター上に限りバリデーションをスキップすることができます.

たとえばJUNOS でdst prefix を指定せずFlowspec を有効にしたい場合,該当ルーターすべてにstatic 設定することで実現できます.ただ,「そこまでするならパケットフィルターを書いて回ったほうがいいんでは」と感じます.

ほかにstatic 設定はFlowspec をオリジネートするためにも使われますが,通常はこっちがメインだと思います.

Neighbor やピアグループ単位でバリデーションを無効にできる (2015-05-28 追記)

AS内だけでFlowspec を使う場合など,厳しいバリデーションが邪魔になることがあります.その場合はNeighbor やピアグループ単位でバリデーションをスキップすることができます.

Juniper vSRX:

protocols {
    bgp {
        group ibgp {
            family inet {
                flow {
                    no-validate skip-validation;
                }
            }
        }
    }
}

policy-options {
    policy-statement skip-validation {
        then accept;  # フィルター条件をいろいろ書ける
    }
}

Cisco IOS-XRv

router bgp 64600
 neighbor 192.168.1.20
  address-family ipv4 flowspec
   validation disable

Juniper vSRX とCisco IOS-XRv のちがい (2015-05-28 追記)

destination prefix 指定のないFlowspec の扱いなどに違いはありますが,一番大きな差は

です.Cisco IOS-XRv はdraft-ietf-idr-bgp-flowspec-oid-02 が実装されており,オリジネーターバリデーションが一部変更されています.

Step (a) of the validation procedure specified in RFC 5575, section 6 is redefined as follows:

        a) One of the following conditions MUST hold true:
           o The originator of the flow specification matches the
             originator of the best-match unicast route for the
             destination prefix embedded in the flow specification.
           o The AS_PATH and AS4_PATH attribute of the flow
             specification are empty.
           o The AS_PATH and AS4_PATH attribute of the flow
             specification does not contain AS_SET and AS_SEQUENCE
             segments.

簡単に言えば「iBGP からFlowspec 経路を受信した場合に限り,上のオリジネーターバリデーションをスキップする」です.オリジネーターがベストマッチ経路と一致しない場合や,ベストマッチ経路が存在しない場合でもvalid になる可能性があります.

RFC9117 (2023-01-31 追記)

現在👆のI-Dは RFC9117 になっています.

datatracker.ietf.org

未確認ですが,多くの実装で iBGP から受信したFlowspec 経路に関するバリデーションをスキップすると思われます。@a16tochjp さんのコメントによると「JUNOSもそうなっている」とのことです.

その他

どう使うか?

今回のIOS-XRv のように期待通り動かないことや,CloudFlare でのトラブル のような例もあります.議論の余地はありますが,現状だと まだ「コミュニティに知見が足りないため検証するしかないが,コストに対して得られるメリットが小さい」場合が多そうです.

一方で「用途を限ればすごく便利で,コスト問題も解決できるかも」と感じています.

たとえば,次のような使い方はよさそう.

  • DDoS 対策など,小さいdst prefix を守るために使う

    • dst prefix が広い場合,more specific バリデーションで落とされる可能性が高い
    • more specific 経路が無ければ大きいdst prefix でも動くが,いちいち確認するのが手間
  • exabgp のようなAPI 豊富なソフトウェアルーターを使い,Flowspec を注入する

    • 自動化のため
  • AS ボーダーでフィルターする目的で,ソフトウェアルーターは1段階だけ下流ルーターとピアする

    • 小さいdst prefix を守りたい場合,その経路はおそらく下流からくる
    • トランスポートアドレスバリデーションにより,AS ボーダーでのみFlowspec が動くと期待できる
  • 特定のルーターでのみ発動させたい場合は,BGP community で制御

強力なので,ハマれば便利そうですが…どうでしょうかね. 「こういう使い方できそう」「こうやってる」などなど,ぜひご意見ください!

*1:1. はMP_REACH_NLRI or MP_UNREACH_NLRI で,2. はExtended BGP community

*2:厳密に言えばこれは間違っていて,たとえばRR - RR client 間に複数BGP セッションがあった場合は,RR から見てどのセッションから受信してもオリジネーターは同じ

*3:仕様なのかバグなのか,未確認

VirtualBox 上の IOS-XRv コンソールに接続する

Cisco IOS-XRv は ハイパーバイザーが開くTTY にコンソールを接続してくれないので,自分で設定する必要がある.

たとえばVMWare FusionコンソールをPTY に繋ぐ機能があるので簡単だが,VirtualBox は面倒.

コンソールをドメインソケットとして出す

下のような設定をする.

socat で接続する

brew install socat
socat UNIX-CONNETCT:/tmp/iosxrv_console stdio,raw,echo=0,escape=0x1a

こんな感じで標準入出力をドメインソケットに接続できる.escape=0x1a^Z で接続を切るための設定.このへん を参考にしながら好みで変えるといいと思う.

ネットワークエンジニアリングはアジャイルじゃない

リーン・スタートアップと言ってもいい.Web 界隈ではあたりまえの開発手法を ネットワークの開発に使うのはむずかしい.

自分はインターネットが得意で (ネットワークとしてのインターネットね!) たかだかIP と上下レイヤーくらいしか分からないんだけど,なにか新機能を作るときの事情はだいたい共通していると思うし,幅広く「ネットワーク」とくくってしまう.実際は共通どころか,エンタープライズネットワークのほうが制約が多いし,IP よりPHY のほうがつらい.まだ自由度があるインターネットでさえ,Web 界隈やミドルウェア,サーバーインフラ界隈のようなスピードで動けない.

うらやましいことに,それらの開発についてはオンライン/オフラインいろんなところで議論され,本が出版され,ノウハウが溜まっていく.一方でネットワーク開発は 低いギアでアクセル踏みっぱなしのような,なんか「タスクこなしてるんだけど進んでない感じ」がする.なんですかこの違い.アプリレイヤーの仕事をしているときとは違ったストレスを感じる.

ネットワークエンジニアリングの現場

ハードウェア / ソフトウェアを使ってネットワークに新機能を足したり,地理的に / 帯域的に拡張したり,運用を変えたりする場合,

  1. どういう技術があるか調査する
  2. テスト環境で粗く試す
  3. 設計する
  4. 必要なハードウェア,ソフトウェアを調達する
  5. 検証環境でちゃんと試す
  6. 必要な回線を調達する
  7. ユーザーから借用をとる
  8. ハードウェア,ソフトウェア,回線をインストールする
  9. デプロイする

みたいなことが必要だが,あるあるネタとして

  • 調達のリードタイムが予測できない / 長い
  • やってみないと,実網にどんな影響が出るかわからない
  • 借用に要する時間が予測できない / 長い

こういう問題をよく見る.たとえばソフトウェア開発と比較すると「ソフトウェアには起こらない問題」と「起こってるんだけど頑張って回避している問題」が含まれていて,ネットワークエンジニアはソフトウェアエンジニアが頑張っているところを盗まないといけないと思う.

もちろん「コード書いて自動化しましょう」もその1つなんだけど,生産性が低い原因はたぶんそれだけではない.

生産性の低さ

自分は,デプロイまでの待ち時間が大きな原因だと思っている.調達とか 会社をまたいで調整しないといけないタスクが挟まっていたり,借用 (お客さんに「メンテしていいですか」と聞いて回る営み) のようなタスクがあるかもしれない.契約上は1ヶ月前に通知すればメンテできるが,慣例で一部法人ユーザーにお伺いを立てたりすることがある.「これ!と決めたやつをデプロイするまでに1ヶ月待たないといけない」なんて普通にある.

そのせいで 年間のデプロイ回数が限られ,フィードバックがないから試行錯誤できず,一発で大成功させようとして準備の時間が増え,待っている間にネットワークが変わって手戻りが発生する. 他者との調整が主なボトルネックだから,ミーティングが増え,資料を作りまくり,調整能力だけがグングン伸びる.

ソフトウェア開発はどうやっているか

アジャイルイテレーションを小さく回せ」リーン・スタートアップ「MVP から始めよ」両方とも,デプロイに時間をかけず フィードバックを短期間で得るための努力があってのことだと思う.大規模にやれば「マージに時間がかかる」「QA チームが求める品質にならない」いろんな問題が出るところを,「CI を回す」「毎週火曜日にリリースすることにする」「組織を変えて,QA 機能をプロジェクト内に持つ」「ベータ提供するしくみ」「一部ユーザーにのみデプロイし,小さく失敗して早く直す」など,技術で解決するのはもちろんのこと 組織 / ワークフロー / ポリシーを見直して頑張ってる.

ネットワーク開発は何ができるか

「調達のリードタイムが予測できない / 長い」というのはしょうがない.でも「在庫を持つコスト」と「持たないことによる生産性の低下」は天秤にかけられるかもしれない.

「やってみないと,実網にどんな影響が出るかわからない」とくにインターネットルーティングは生き物だからしょうがないけど「自分たちのネットワークは複雑だから,やってみないとわかりません」は避けないといけない.シンプルに保つ努力は必要だし,インターネットの端っこにテストベッドを持つ選択肢もあるし,堅く作る前のベータサービスに付き合ってくれるユーザーがいるかもしれない.

「借用に要する時間が予測できない / 長い」レイヤーが低いほど上に乗っているものが多い = 影響範囲が大きいので,メンテナンスには慎重になるべき.でもメンテナンスポリシーが曖昧だと「うーん,分からんけど安全側に倒して…」ってなるし「契約ではできることになってるけど,いままでそうしてなかったから…」みたいなのも癌だと思う.

ちなみに

いま,週のうち数日はフリーのネットワークエンジニアとして働いており,タスクをガッとこなす日と何もしない日が極端に分かれている.それでも自分がボトルネックになるケースは少ない (はず!) ので,待ちが多いと時間を区切って生産性を上げるという手が効く.そういう意味では,トヨタのリーン生産方式の方は勉強しないとなあ,と思ってる.

Time Attack: bgpsimple vs exabgp 2nd Heat

After @exabgp thankfully gave me an advice on my previous post, I carried out performance tests of bgpsimple and exabgp again.

Right, API might be faster than massive config loading.

Full Route Advertisement

Service providers are struggling with the growing IPv4 full route and recently some people abandon default-free zone due to TCAM space problem. But they still sometimes need full route for analysis and forensic purpose. So, I'm curious, in such a case, is there good toolbox to inject full route into their network?

I think that simple BGP daemons are good options,

  • bgpsimple
  • exabgp with mrtparse

can read a full-table bgpdump archive and advertise to neighbor routers. Then, how fast are they?

Test Equipments

Firefly was configured as a dumb BGP neighbor rejecting any route so that it couldn't affect route injector's performance.

Benchmark

bgpsimple exabgp with config file exabgp with API
Until BGP turnup 0'00" 2'58" 0'02"
To complete advertising since BGP turnup 2'38" 1'13" 4'13"
To complete advertising since "clear bgp neighbor" 2'43" 2'09" 2'31"

Version tested: *1

  • bgpsimple 0.12 + perl 5.14.2
  • exabgp 3.4.10 + python 2.7.3

It took bgpsimple 2'38" to advertise full route, while exabgp required 4'11" ~ 4'15" in total.

exabgp shows different behavior depending on its configuration. exabgp with config file in the table above means "a bunch of static routes configured in .conf converted by mrtparse", exabgp with API means "configured to run an external script for route injection". The script looks like:

import sys
import time

messages = [
    'announce route 1.0.0.0/24 origin IGP as-path [2497 15169 ] next-hop 192.168.0.78',
    ...
]

while messages:
    message = messages.pop(0)
    sys.stdout.write( message + '\n')
    sys.stdout.flush()

while True:
    time.sleep(1)

See exabgp's wiki for more details.

Conclusion

bgpsimple is handier and faster way to inject full route from bgpdump archive. It simply assumes that the route information is given as a file in bgpdump -m format, read it with no signal handling and process routes in a simple loop with generating a minimal set of objects.

*1:Different version of exabgp from the previous post

ruby で pcap を読む

tcpdumptshark などでキャプチャーしたパケットを,ruby で読むためのライブラリがあります.

The Ruby Toolbox などで検索すると山のように出てきますが,いくつか良さそうなのを紹介します.

PacketFu

例: IP パケットのsource address だけを抽出したい場合:

require 'packetfu'

packets = PacketFu::PcapPackets.new.read(File.read('sample.pcap'))

packets.each do |p|
  packet = PacketFu::Packet.parse(p.data)
  puts packet.respond_to?(:ip_saddr) && packet.ip_saddr
end
  • PacketFu::Packet#inspect がちゃんとあってprint デバッグしやすい
[1] pry(main)> p packet
--EthHeader-------------------------------------------------
  eth_dst      fe:ff:20:00:01:00       PacketFu::EthMac
  eth_src      00:00:01:00:00:00       PacketFu::EthMac
  eth_proto    0x0800                  StructFu::Int16
--IPHeader--------------------------------------------------
  ip_v         4                       Fixnum
  ip_hl        5                       Fixnum
  ip_tos       0                       StructFu::Int8
  ip_len       48                      StructFu::Int16
  ip_id        0x0f41                  StructFu::Int16
  ip_frag      16384                   StructFu::Int16
  ip_ttl       128                     StructFu::Int8
  ip_proto     6                       StructFu::Int8
  ip_sum       0x91eb                  StructFu::Int16
  ip_src       145.254.160.237         PacketFu::Octets
  ip_dst       65.208.228.223          PacketFu::Octets
--TCPHeader-------------------------------------------------
  tcp_src      3372                    StructFu::Int16
  tcp_dst      80                      StructFu::Int16
  tcp_seq      0x38affe13              StructFu::Int32
  tcp_ack      0x00000000              StructFu::Int32
  tcp_hlen     7                       PacketFu::TcpHlen
  tcp_reserved 0                       PacketFu::TcpReserved
  tcp_ecn      0                       PacketFu::TcpEcn
  tcp_flags    ....S.                  PacketFu::TcpFlags
  tcp_win      8760                    StructFu::Int16
  tcp_sum      0xc30c                  StructFu::Int16
  tcp_urg      0                       StructFu::Int16
  tcp_opts     MSS:1460,NOP,NOP,SACKOK PacketFu::TcpOptions
  • C拡張ではないため 遅い

pcap / ruby-pcap

例: IP パケットのsource address だけを抽出したい場合:

require 'pcap'

packets = Pcap::Capture.open_offline('sample.pcap')

packets.each do |packet|
  puts packet.ip? && packet.ip_src
end
  • pcap,ruby-pcap の中身は同じ
  • ruby っぽく書ける
  • C拡張

このgem はruby 2.2 に対応してません.(2015-07-27 追記: 対応されました) 本体に取り込まれるまでは

gem install specific_install
gem specific_install https://github.com/codeout/ruby-pcap

or

git clone https://github.com/codeout/ruby-pcap
cd ruby-pcap
gem build pcap.gemspec
gem install --local pcap-0.7.7.gem

でインストールできます.

ほか

  • pcaprub
  • ffi-pcap
  • ffi-packets
  • blackfoundry-pcap
  • pcap-ffi
  • spcap
  • pcap_simple

いろいろありますが,各種プロトコルヘッダーにアクセスするAPI が少なくて使いやすいとは言えません.

ベンチマーク: PacketFu vs. pcap

10,000 パケットの処理時間:

              user     system      total        real
PacketFu  3.760000   0.040000   3.800000 (  3.857046)
pcap      0.010000   0.000000   0.010000 (  0.012067)

やはり差が出ます.パフォーマンスが要求される場合は pcap オススメ.

ベンチマーク用コード:

require 'packetfu'
require 'pcap'
require 'benchmark'

Benchmark.bm do |x|
  packets = PacketFu::PcapPackets.new.read(File.read('sample.pcap'))

  x.report('PacketFu') do
    packets.each do |p|
      packet = PacketFu::Packet.parse(p.data)
      packet.respond_to?(:ip_saddr) && packet.ip_saddr
    end
  end

  packets = Pcap::Capture.open_offline('sample.pcap')

  x.report('pcap') do
    packets.each do |packet|
      packet.ip? && packet.ip_src
    end
  end
end

別途PacketFu をプロファイリングしてみても,パケットのparse が遅そうです.

  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
  6.44    10.16     10.16   297491     0.03     0.05  StructFu::Int#read
  4.46    17.20      7.04   171613     0.04     0.06  PacketFu::EthPacket.can_parse?
  3.33    22.45      5.25    70757     0.07     0.15  PacketFu::IPPacket.can_parse?
  3.02    27.22      4.77   296710     0.02     0.02  PacketFu.force_binary
  3.00    31.96      4.74    44296     0.11     0.18  PacketFu::EthOui#read
  2.45    35.83      3.87    12958     0.30     4.20  PacketFu::IPHeader#read
  2.30    39.46      3.63     8866     0.41     2.22  PacketFu::TCPPacket#tcp_calc_sum
  2.17    42.89      3.43  1149060     0.00     0.00  String#[]
  2.09    46.19      3.30    17732     0.19     1.64  PacketFu::TcpOptions#read
  2.09    49.49      3.30    27390     0.12     0.30  PacketFu::TcpOption#read
  2.08    52.77      3.28    79465     0.04     0.06  PacketFu::Packet.layer
  1.93    55.81      3.04   155600     0.02     0.03  StructFu::Int#to_i
  1.84    58.72      2.91   966615     0.00     0.00  Struct#[]
  1.83    61.61      2.89   159701     0.02     1.18  PacketFu::Packet.parse
  1.80    64.45      2.84     8866     0.32     4.71  PacketFu::TCPHeader#read
  1.79    67.28      2.83   278978     0.01     0.03  Struct#force_binary

Time Attack: bgpsimple vs exabgp

お手軽フルルート環境に興味が出て,どの程度お手軽にできるのか試しました.

(参考: Other OSS BGP implementations · Exa-Networks/exabgp Wiki · GitHub)

をざっと見て

  • MRT から経路注入できる
  • eBGP で渡りそうなPath Attribute を付けられる
  • 手数が少ない *1

ので選ぶと,bgpsimple / exabgp が候補になります.

bgpsimple,Google Code にしかないっぽいんだけど 大丈夫か...

フルルート広告の所要時間が気になる

bgpsimple はbgpdump でMRT table dump をテキスト化すれば入力できるし,exabgp はmrtparser で設定ファイルを出力できます.両方お手軽なんですが,経路広告の所要時間が気になるので計測してみました.

環境

経路受信側は別Hypervisor のfirefly.ボトルネックにならないよう,経路はすべてreject する設定.

結果

bgpsimple exabgp
ピアが上がるまで 数秒 3'59"
フルルート広告し終わるまで 2'39" 1'15"
clear bgp 後,フルルート広告し終わるまで 2'34" 2'57"

bgpsimple のほうがトータル速そう.exabgp は設定ファイルをload → parse するのに4分もかかるため,全体として2倍遅いです.

注意

bgpsimple を使うときはholdtimer を長く

bgpsimple は経路を広告し終わるまでKEEPALIVE メッセージを送らないため,経路広告が終わるまえにHold Timer Expire する可能性が高い.

exabgp は適宜KEEPALIVE してくれます.

bgpsimple は,経路リストの全エントリーについて広告

bgpdump -m によってMRTをテキスト変換してbgpsimple に入力しますが,もとのMRT には複数neighbor 分の経路が保存されています.そのため,テキスト変換された経路リストには同じprefix の経路が複数含まれている場合があります.これを順次UPDATE メッセージとして送信すると,対向のfirefly では後に受け取ったpath だけがAdj-RIBs-In に残ります.

ムダですね.

exabgp はMRT ファイル上で先に現れた経路のみ広告

mrtparser の仕様により,prefix が同じであれば先に読まれたpath のみUPDATE メッセージとして送信します.ムダはありませんが,bgpsimple で経路広告した場合と比べて 対向のAdj-RIBs-In の中身が変わってくることに注意です.

*1:たとえばeggpd はerlang でフルルート分の設定を書けば要件を満たすが,変換コードを書かないといけないのでパス