まえがきでは、システム監視ソフトウェアのMuninを簡単に紹介しました。また、ネットワーク通信の詳細な統計をとる技術であるNetFlowについても少し述べました。今回からは、NetFlowによるネットワークの統計情報生成から、本情報を可視化するまでの流れを説明していきます。
本記事では、FreeBSDマシンを対象にNetFlow exporter、すなわちNetFlowによるトラフィック統計情報生成および送信機能(下図青色部分)、をセットアップする手順を説明します。

まえがきにも述べましたが、FreeBSDカーネルにはNetFlowデータを生成する機能が組み込まれています。したがって、NetFlow exporterを設定するにあたり、追加のソフトウェアをインストールする必要はありません。NetFlowデータ生成機能は、netgraphと呼ばれるフレームワークにそって動作するng_netflow(4)カーネルモジュールが提供します。
Netgraphとは
NetFlow exporterを設定する前に、少しだけnetgraphフレームワークについて見ておきましょう。
マニュアル(man 4 netgraph)によると、netgraphは「多様なネットワーク機能を実現するオブジェクト(グラフノード)を実装するための、統一的かつモジュール化された方法を提供する」とあります。
Netgraphフレームワークでは、グラフのノードに相当するオブジェクトが単一のネットワーク機能を実現します。ノードにはフックと呼ばれる「手」が一つ以上備わっています。ノード同士が手をつなぐ、すなわちノードに備わったフックとフックとを接続することにより、グラフのエッジが構成されます。このようにして、順次ノードをエッジで連結していくことで、より複雑なネットワーク機能を実現するわけです。
Unix的な考え方になじみのあるかたであれば、単一かつシンプルな機能を実現するコマンドをパイプで接続して、より複雑な処理を実現していくのと同様だというとわかりやすいでしょうか。コマンドに相当するのがノード、パイプなどのプロセス間通信路に相当するのがフック同士を結んだエッジということになります。
抽象的な話ばかりではうんざりしてしまいますね。netgraphのさらなる詳細についてはマニュアルを参照いただくとして、次からはNetFlow exporterを設定する具体的な手順を説明していきます。
Netgraphを用いたNetFlow Exporter
netgraphフレームワークベースのNetFlowモジュールですが、本フレームワークにもとづいて動作するモジュール群を統一的に制御するコマンドがngctl(8)です。NetFlow exporterを設定する手順をひとことで言うと、ngctlコマンド(とそのサブコマンド群)を駆使してNetFlow exporterの機能を実現するグラフを作り上げる、ということになります。
使用する具体的なコマンドについては、以下の二つの記事に説明があります。
- CONFIGURE IN KERNEL NETFLOW EXPORT WITH netgraph(4) (The FreeBSD Forums)
- NetFlow v9 Exporting from FreeBSD routers/firewalls (Dan’s Blog)
記事をご覧になっていかがでしょうか? スムーズに理解できたでしょうか?
わたしはというと、カーネルモジュールをロードするところはOKです。しかし、ngctlコマンドがいけません。数個のサブコマンドを実行するのですが、その字面を見ただけではどんなグラフができるのかイメージできませんでした。
そこで、なんとか理解を進めるために、サブコマンドの実行にともなうグラフの変化をステップごとに図にしてみました。以下、作成した図にもとづいて、手順を一つづつ説明していきます。また、本記事の最後に自動実行用のスクリプトを用意していますので、詳細な説明が不要な場合はそちらにジャンプしてください。
カーネルモジュールのロード
まず、関連するカーネルモジュールをロードします。今回は、イーサネットインターフェイスに対応するng_ether(4)、NetFlowによる統計情報を生成するng_netflow(4)、および生成された統計情報をネットワーク経由で送信するng_ksocket(4)の各モジュールを使用します。
以下のコマンドを実行してカーネルモジュールをロードします。
kldload netgraph ng_ether ng_netflow ng_ksocketngctlコマンドを用いたグラフ構築
次からは、ngctlコマンドを用いてグラフを作成していきます。
注: 以下のコマンドを一つづつ実行する場合、Step 1のmkpeerコマンドを実行すると同時にネットワーク接続が切れます。したがって、(ssh経由などではなく)コンソールで実行するようにしてください。
Step 0 (初期グラフ)
実は、カーネルモジュールをロードした時点で自動的に作成されるnetgraphノードがあります。
マシンが備えるイーサネットインターフェイスに対応するng_etherタイプのノードが自動的に作成されます(下図)。
本ノードの名前はインターフェイス名と同じ(下図の場合はem0)になります。また、本ノードにはlower, upper, およびorphansという三つのフックがあります。(orphansは今回まったく使用しませんので、忘れていただいても大丈夫です。)

この時点では、それぞれのフックに何もノードがつながっていませんので、送受信されるパケットは本ノードを素通りしていきます。
Step 1 (ng_netflowタイプノードの作成)
# ngctl
Available commands:
(snip)
+ mkpeer em0: netflow lower iface0では、ngctlコマンドを使ってグラフを作成していきましょう。本コマンドを実行すると、使用可能なサブコマンドのリストと+プロンプトが表示されます。+プロンプトのところで、実行したいサブコマンドを入力していきます。
まず、mkpeerコマンドを用いて、NetFlow統計情報を生成するng_netflowタイプのノードを生成します。この際、ノードem0のlowerフックと新規生成ノードのiface0フックとを接続します(下図)。

この時点ではノードem0のupperフックと新規作成ノードのout0フックが接続されていないので、カーネルとイーサネットインターフェイスのあいだでパケットが流れない(= 接続が切れている)状態になっていることに注意してください。
Step 2 (ng_netflowタイプノードへ名前付け)
+ name em0:lower netflow作成しただけの状態ではノードに名前がついていません。そこで、nameコマンドを用いてStep 1で作成したノード、つまりノードem0のlowerフックにつながっているノードにnetflowという名前をつけます(下図)。
注: netflowという名前をつけましたが、これは任意の名前でかまいません。

Step 3 (em0とnetflowのフックの接続)
+ connect em0: netflow: upper out0次に、connectコマンドを用いて、ノードem0のupperフックとノードnetflowのout0フックとを接続します(下図)。

この時点で、カーネルとイーサネットインターフェイスの間で再びパケットが流れるようになりました。
ここまでで、グラフノードの作成、ノードへの名前付け、フック同士の接続という基本的なコマンドが出てきましたね。これで、送受信されるパケットがすべてノードnetflowを通るようになり、NetFlow統計情報の生成が始まりました。
次は、ノードnetflowが生成したNetFlow統計情報を外部に送信するためのノードを作成しましょう。
Step 4 (ng_ksocketタイプノードの作成)
+ mkpeer netflow: ksocket export9 inet/dgram/udpnetflowの時と同様、mkpeerコマンドを用いて今度はng_ksocketタイプのノードを生成します。この際、ノードnetflowのexport9フックと新規生成ノードのinet/dgram/udpフックとを接続します(下図)。
注: ng_netflowタイプのノードには、NetFlow統計情報を外部に送信するためのフックとしてexportおよびexport9の二つが備わっています。exportはNetFlow v5データの送信、export9はNetFlow v9データの送信に用います。本記事ではIPv6のトラフィックに関する統計情報も取りたいので、NetFlow v9を送信するexport9フックを使用しました。

Step 5 (ng_ksocketタイプノードへ名前付け)
+ name netflow:export9 nfsock作成しただけではノードに名前がついていませんので、ノードnetflowのときと同じく、nameコマンドを用いてStep 4で作成したノード、つまりノードnetflowのexport9フックにつながっているノードにnfsockという名前をつけます(下図)。
注: 実行するコマンドはあと一つだけなので、わざわざng_ksocketタイプのノードに名前をつける必要は実はありません。しかし、名無しというのもなんとなく気持ち悪いので、とりあえず名前をつけることにしました。

Step 6 (Flow Collectorへデータ送信開始)
+ msg nfsock: connect inet/<ipaddress>:<port>最後のコマンドです。msgコマンドは指定したノードに対して、指定した制御メッセージを送るコマンドです。送る制御メッセージはconnectです。本メッセージはinet/dgram/udpフックを通じて受け取ったデータ、すなわちNetFlow統計データを、メッセージにおいて指定したIPアドレスおよびポート宛に送信するよう指示します(下図)。

まとめとして、これまでに述べた各ステップをアニメーション化してみました。(すみません、単にAnimated PNGを使ってみたかったんです。)

自動起動の設定
はぁ、長かったですね。あとから見てみると、そんなに複雑なことをやっているわけではないことがわかりますね。ステップごとに図を描いてみることでようやく理解できました。
さて、以上でNetFlow exporterの設定ができたわけですが、FreeBSDを起動するたびにいちいち手動で上記の手続きを行なうのはとてもめんどうです。そこで、マシンの起動時にNetFlow exporterが自動的に設定、起動されるようにスクリプト化を行ないました。(といっても、ほとんどこのスクリプトをコピーしただけですが。)
スクリプトを使用するには、まず適当なディレクトリに上記のリポジトリをクローンしてください。その後、ng_netflowファイルを/usr/local/etc/rc.d以下にコピーします。
cd <適当なディレクトリ>
git clone https://github.com/tagattie/FreeBSD-Netflow-Export.git
cp ./FreeBSD-Netflow-Export/ng_netflow /usr/local/etc/rc.dその後、以下の各コマンドを実行して、自動起動設定、NetFlow統計対象のネットワークインターフェイス指定、NetFlow collectorのIPアドレス、およびポート番号(オプション)を指定します。
sysrc ng_netflow_enable=YES # システム起動時にNetFlow exporterを自動起動
sysrc ng_netflow_interface=em0 # NetFlow統計生成対象のネットワークインターフェイス
sysrc ng_netflow_collect_addr=X.X.X.X # NetFlow collectorのIPアドレス
sysrc ng_netflow_collect_port=XXXX # デフォルト(4444)以外を使用する場合のみあとはFreeBSDを再起動するか、service ng_netflow startを実行すればOKです。
次回の記事では、NetFlowデータを受信し蓄積するFlow Collectorの設定手順について説明します。
参考文献
- All About Netgraph, https://people.freebsd.org/~julian/netgraph.html
- CONFIGURE IN KERNEL NETFLOW EXPORT WITH netgraph(4), https://forums.freebsd.org/threads/howto-monitor-network-traffic-with-netflow-nfdump-nfsen-on-freebsd.49724/#post-277772
- NetFlow v9 Exporting from FreeBSD routers/firewalls, https://www.dan.me.uk/blog/2016/06/01/netflow-v9-exporting-from-freebsd-routersfirewalls/