NetFlowプロトコルトラフィック情報収集システムは、次のコンポーネントで構成されています。
- センサー デバイスを通過するトラフィックの統計を収集するデバイス(ルーター、L3スイッチ)。
- コレクター 。 センサーからデータを収集し、ストレージに入れます。
- アナライザー 。 コレクターが収集したデータを分析し、レポートを生成します。
C#のアナライザー機能の一部の開発、具体的にはNetFlowパッケージの分析について説明します
MikroTikルーターがセンサーとして使用されました。
ether1インターフェイスのNetFlowをオンにします。
/ip traffic-flow set enabled=yes interfaces=ether1
そして、コレクターを追加します(原則として、コレクターはポート2055、9555または9995でリッスンします):
/ip traffic-flow target add disabled=no version=9 address=192.168.0.100:9995
または同じことですが、 WinBoxを介して:
これで、 UDPポート9995で192.168.0.100の IPアドレスを持つコンピューターでは、バージョン9のNetFlowパケットがUDP (またはSCTP )に到着します。 パッケージには、動作するものがあります。
着信パケットを解析する
プロトコル仕様を検討した結果、各NetFlowパケット(Nバイト)が以下で構成されていることがわかります。
- パケットヘッダー (20バイト)-フィールドを含む単一のコピーのパケットヘッダー:
- バージョン番号 ( UInt16-2バイト) -NetFlowバージョン番号。常に9です。
- カウント ( UInt16-2バイト)-レコードの総数。 さらに、この分野の記事は冒険でした。
- sysUpTime (UInt32-4バイト)-デバイスの開始からのミリ秒単位の時間-UpTime。
- UNIX秒 ( UInt32-4バイト)-パケットが送信された0000 UTC 1970からの秒数。
- シーケンス番号 ( UInt32-4バイト)-送信されたパケットのカウンター。パケットごとに常に増加しているため、それらの間のパケットが失われたかどうかを確認できます。
- ソースID ( UInt32-4バイト)-データストリーム番号。実際には、センサー側から複数のデータストリームを送信できます。
- FlowSet (N-20バイト)-テンプレート、データ... FlowSetは複数または1つにすることができます。 各FlowSetには、送信されるデータのタイプ(テンプレート、データ)から変更されていない2つのフィールドがあります。
- FlowSet ID ( UInt16 2バイト)-テンプレートの場合は常に0、オプションのテンプレートの場合は1、データの場合はテンプレートIDと等しいため、255を超える(256〜65535)。
- 長さ ( UInt16 2バイト) -FlowSet全体のサイズとFlowSet IDおよびLengthフィールド。
- 転送されるデータのタイプに応じたその他のフィールド。
テンプレートを含むFlowSet IDを見て、 FlowSet IDフィールドで始まり、次にLength 、次に:
- テンプレートID ( UInt16 2バイト)-データが送信される各テンプレートの一意のID。 256から65535の数;
- フィールド数 ( UInt16 2バイト)-テンプレート内のフィールドの数。 次に、フィールドタイプ( Field Type )とサイズ( Field Length )があります。
- フィールドタイプ ( UInt16 2バイト)- フィールドのタイプを指定する数値。 すべてのタイプはプロトコル仕様にあります;
- フィールドの長さ -バイト単位のフィールドの長さ。
データを含むFlowSet IDを見て、 FlowSet IDフィールドで始まり、次にLength 、次に:
- データ...フィールドとそのサイズに対応するデータ。
- パディング -4バイトの境界までゼロを埋めます。
いわゆるオプションのテンプレートとデータもあります。 私はそれらを考慮しません、彼らは私に会っていません、この理由で実装にライブラリがありませんが、すべてを追加することができます。
UMLクラス図をコンパイルしました( NClassを使用):
またはpdfで
そして、彼は入ってくるパケットを解析するライブラリを書きました。
すべてが開始するメインクラスはPacketです。 その唯一のコンストラクタは、バイト単位の受信NetFlowパッケージと、現在のテンプレートのリストであるTemplatesクラスのオブジェクトを受け入れます。
次に、 Parse関数がPacketsクラスのコンストラクターで呼び出され、 Templatesクラスのオブジェクトを受け取ります。
この関数は、パケットをヘッダー(20バイト)に分割し、 Headerクラスを介してさらに処理します。 対応するFlowSetクラスへの処理のためのFlowSetの各FlowSetの転送。
いくつかのFlowSetが存在する可能性があるため、パケットの2番目の部分(ヘッダーの20バイトなし)を分析し、異なるFlowsetに分割する必要があります。 MikroTikのパッケージ内の単一コピーにあるFlowSetが、 C#でNetflow Simulatorを使用すると、パッケージ内に複数のFlowSetがあるパッケージで作業できることが注目に値します。 さらに、彼のおかげで、 MikroTikのNetFlow v9の実装で面白いバグが見つかりました。詳細については、 こちらをご覧ください 。
C#のNetflow Simulator :
FlowSet`sのパッケージの一部を壊すコードスニペットを次に示します。
this._flowset = new List<FlowSet>(); Int32 length = _bytes.Length - 20; Byte[] flowset = new Byte[length]; Array.Copy(_bytes, 20, flowset, 0, length); byte[] reverse = flowset.Reverse().ToArray(); int templengh = 0; while ((templengh + 2) < flowset.Length) { UInt16 lengths = BitConverter.ToUInt16(reverse, flowset.Length - sizeof(Int16) - (templengh+2)); Byte[] bflowsets = new Byte[lengths]; Array.Copy(flowset, templengh, bflowsets, 0, lengths); FlowSet flowsets = new FlowSet(bflowsets, templates); this._flowset.Add(flowsets); templengh += lengths; }
Headerクラスでは、パッケージヘッダーがフィールドに解析されます。 これを行う前に、ヘッダーが逆になります。
this._bytes.Reverse().ToArray();
次に、ビットをフィールドのタイプ(たとえば、バージョンフィールド)に変換します。
this._version = BitConverter.ToUInt16(reverse, this._bytes.Length - sizeof(Int16) - 0);
はい、 ヘッダーフィールドsysUpTimeのタイプはTimeSpan です。このタイプに変換できます。
get { return new TimeSpan((long)this._uptime * 10000); }
UNIX SecsフィールドのタイプはDateTimeです。
get { return new DateTime(1970, 1, 1).AddSeconds(this._secs); }
FlowSetの処理に移りましょう。 FlowSet IDおよびLengthフィールドを受け取った後、残りのフィールドはFlowSet IDに応じて解析されます 。 0または1の場合、これはパターンであり、256から65535の数値の場合、これはデータです。
これがテンプレートの場合、その処理をTemplateクラスに渡し、同じIDを持つテンプレートの存在をテンプレートストア( Templatesクラスのオブジェクト)で確認して置き換えます。そうでない場合は、テンプレートを追加します。
このデータの場合、ストレージ( Templatesクラスのオブジェクト)にそのようなテンプレート( FlowSet ID == Template ID )が存在するかどうかを確認し、存在する場合は、このテンプレートをDeepClone関数でコピーし、 フィールド-Fieldに入力します。そうでない場合は、テンプレートがないため何もしませんバイトのセット。
DeepClone関数:
public static object DeepClone(object obj) { object objResult = null; using (MemoryStream ms = new MemoryStream()) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(ms, obj); ms.Position = 0; objResult = bf.Deserialize(ms); } return objResult; }
フィールドはフィールドであり、次のパラメーターがあります。
- タイプ -タイプ;
- 長さ -サイズ;
- 値 -値。
また、リポジトリ内のテンプレートの フィールドには、 値パラメータがありません。 値は空ですが、 FlowSetでテンプレートパッケージを処理する場合、 Packetオブジェクトには既にValueフィールドが含まれています。
これに加えて、 FieldType列挙もあります-型名がこの型の番号に対応する列挙です。 ( フィールドの 型パラメーター)
このライブラリが機能するための例が作成されました。
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetFlow; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace Consoles { class Program { static void Main(string[] args) { Templates _templates = new Templates(); Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9995); sock.Bind(iep); EndPoint ep = (EndPoint)iep; byte[] data = new byte[2048]; while (true) { int recv = sock.ReceiveFrom(data, ref ep); Console.ReadKey(); Console.Clear(); byte[] bytes = new byte[recv]; for (int i = 0; i < recv; i++) bytes[i] = data[i]; Packet packet = new Packet(bytes, _templates); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(packet.ToString()); } sock.Close(); Console.ReadKey(); } } }
ソケットを作成し、PCのUDP 9995ポートでリッスンします。 _templatesはテンプレートリポジトリです。 到着した各パケットをPacketクラスのパケットオブジェクトに送り、テンプレートストレージも渡します。 次に、 packet.ToString()を実行しますこのオーバーロードされた関数は、パケットの内容を表示し、すべてが正常に機能することを確認するためにのみ必要です。
ライブラリはこれですべてです。NetFlowプロトコルを使用してTraffic Analyzerをさらに記述するために使用できるようになりました。
MikroTikの例:
リポジトリにテンプレートのないパッケージを取得しました:
センサーからテンプレートを取得しました:
リポジトリにテンプレートがあるデータを取得しました:
MikroTikの NetFlow v9の実装エラー
このトピックを分析する過程で、 MikroTikのNetFlow v9の実装でエラーが見つかりました。 エラーの本質:
パケットヘッダー( パケットヘッダー )のカウントフィールドには以下が含まれます。
カウント
エクスポートパケット内のレコードの総数。
オプションFlowSetレコード、テンプレートFlowSetレコード、および
データFlowSetレコード。
つまり すべてのFlowSetのすべてのレコードとMikroTikのすべてのレコードが含まれます。このフィールドは、複数のテンプレートまたはデータが転送される場合でも、常に1に等しくなります(上記のスクリーンショットを参照)。 つまり MikroTikのロジックによると、 CountフィールドはFlowSetsの数(レターで私に書いたもので、スクリーンショットから見ることができます)であり、仕様にあるように、すべてのテンプレートとデータの総数に等しくなければなりません。 このため、パケット解析でCountフィールドを使用するのは困難です。
C#のNetflow Simulatorの例を次に示します ( Ciscoからもデータを受信したいのですが、そのような機会はありません。読者の1人がこれをチェックするかもしれません)。
リポジトリにテンプレートのないパッケージを受け取りました( Countに注意してください):
センサーからテンプレートを取得しました(同時に2つのFlowSetsがありますが、 MikroTikが発生しないことが判明しました。 カウントに注意してください。テンプレートは7 = 1、データレコードは6です。MikroTikのロジックによると、 Countは2 = 2 FlowSet `a):
リポジトリにテンプレートがあるデータを取得しました( Countに注意してください):
繰り返しますが、 Wiresharkのパケットフィールドカウントはマークされています:
繰り返しますが、 Cisco Wiresharkのomで画面を送信するすべての人に非常に感謝します。 ここに入れます。
ソースコードはこちらから入手できます 。
作成がガイドされたとき:
ウィキペディアの資料:Netflow
Caligare:NETFLOWとは?
プロトコル仕様バージョン9
今日(19:05 07/30/2013)
MikroTikは答えました:
MikroTikサポート[Dzintars] support@mikrotik.com
こんにちは
問題を報告していただきありがとうございます(記事の著者が正しく指摘しました
netflowパケットヘッダーのカウント値が常に正しく設定されているとは限りません)。 の
この問題は、RouterOSの次のバージョンで修正される予定です。
よろしく、
ジンター
UPD: RouterOS 6.2では、このバグは修正されました。
今日(13:22 09/13/2013)
MikroTikの書き込み:
MikroTikサポート[Dzintars] support@mikrotik.com
こんにちは
NetFlow V9カウントフィールドの問題はバージョン6.2で修正されました
よろしく、
ジンター