LGTM

Looks Good To Me

sFlow は面倒くさいが、fluent-plugin-sflow を再実装する話

背景

ネットワークデバイスからの xFlow を Fluentd で集めて分析、あるいは可視化するということをよくやります。

ルーター製品であれば fluent-plugin-netflow が使えますが、スイッチ製品はそもそも NetFlow がサポートされていません。代わりに sFlow に対応のデバイスが多いものの、現実的に動く fluent-plugin がありません。

NetFlow に比べて sFlow 実装が面倒なためですが、取り組んでみると まあまあ収まりのいい形で実装できた気がする、という記事です。

sFlow の面倒くさい点

パケットデコーダーを内包しないといけない

sFlow は L2 情報が中心で、 L3 + ルーティングの情報は普通ありません。 Ethernet ペイロードの先頭 N バイトをコレクター側でデコードしないといけません。さらに、場合によっては下のような情報を付加してやる必要があります。

  • Prefix Length
  • SRC / DST AS 番号
  • Protocol Nexthop
  • 論理 ifIndex

パフォーマンス

sFlow は IP フローを意識していません。 NetFlow ではうまく Aggregate して Export パケットを減らしたりできますが、 sFlow では Export パケットが増えがちです。

デコードしないといけないわ pps 多いわで、 NetFlow よりパフォーマンスが問題になりやすいです。

sFlow と NetFlow の名寄せ

これは sFlow の問題ではありませんが、 sFlow と NetFlow を同じ基盤で扱うときに面倒な点です。

同じ情報でも、 sFlow の流儀と NetFlow の流儀はちがいます。たとえば、パケットが入ってくるインターフェイスの ifIndex は、 sFlow v5 では input、 NetFlow v9 では input_snmp になっています。

コレクター実装を考えるとき、単純に名寄せすればいいかもしれません。が、「どっちに寄せれば…?」で悩みます。

DNS クエリーはもっと悩ましいです。

NetFlow では

{
  "protocol":    17,  # or 6
  "l4_dst_port": 53
}

で Export されますが、 sFlow では

{
  "protocol":     17,
  "udp_dst_port": 53
}

もしくは

{
  "protocol":     6,
  "tcp_dst_port": 53
}

で出たりします。UDPTCP で別キー。*1 面倒くさい!!

コレクターで UDP / TCP Port を結合して Port にするかどうかは…悩ましい。ネットワークオペレーターの事情に応じて record_transformer プラグインなどで名寄せすればよかろうと思い、今回の実装では sFlow の流儀のまま Export することにしました。

もうひとつ悩ましい例は Export パケットの SRC IP アドレス。sFlow にも Netflow にも該当するフィールドはありません。*2

fluent-plugin-netflow ではそれをhost として格納していますが、 sFlow ではhost は使えません。 HTTP HOST ヘッダーを格納するフィールドとして規定されているためです。。。面倒くさい!

このような流儀の違いが山のようにあります。 filter プラグイン名寄せしつつ、、、でパフォーマンスが出せるか十分に検討していませんが、とりえあず sFlow 流儀に従いました。

既存 fluent-plugin-sflow の BinData 問題

これも sFlow 自体の問題ではありませんが、実は fluent-plugin-sflow という gem は既に存在します。今回はそれを再実装した という形なのですが、既存のものにはいくつか問題があります。

  1. 利用しているパケットデコーダーがメンテされていない → fluent-plugin-netflow と併用できない
  2. スループットが出ない

1 . は最新の BinData では動かないのが根本原因で、仕様上むずかしそう。 2. も BinData が原因です。 ちなみに、fluent-plugin-netflow も当初は BinData ベースでしたが、パフォーマンスのために 低レベルなビット演算に変わったという経緯があります。*3

実装について

InMon 謹製の sflowtool に手を加え、 C 拡張として内包することにしました。

  • 公式のパケットデコーダーを使える
    • 頻度高くないが、まだメンテされているように見える
  • パフォーマンス改善が見込める
  • BinData とさよならできる

のがポイント。実装は こちら

ベンチマーク

手元の Macbook Pro Mid 2015 に 外から sFlow v5 を Export しました。

records / sec
再実装版 14202
オリジナル版 220

まあまあ実用には耐えそう。

TODO

  • テストを足す
    • sflowtool にパッチしたとはいえ、バグを埋めているかもしれない
    • 「動かないよ」という方がいらっしゃれば、ぜひ .pcap ください 🙇
  • fluent-plugin-sflow メンテナー様にコンタクトしてみる
    • 別実装なので…別 gem にするべきか、プルリクしてもいいものか思案中 💦
    • 出力フォーマットが変わる → プルリクするならば互換性を壊す
  • BGP の情報をインポートする

*1:sflowtool の実装

*2:NetFlow v9 の source_id、sFlow の agent_id は Export パケットの SRC IP アドレスではありません

*3:NetFlow v9 はまだ BinData = 遅い