BPF Compiler CollectionでTCPのコネクションをトレースする

まず、Berkeley Packet FilterとBPF Compiler Collectionについて簡単に説明していきます。(以下ではそれぞれをBPFとBCCと略します。)

BPFとは

BPFは独自の命令セットを持つVMです。ユーザーランドのコードをカーネル内で安全に実行することができます。”The BSD Packet Filter: A New Architecture for User-Level Packet Captur”によると、1992年時点ではパケットフィルタリングのためのものだったようです。現在はセキュリティやトレーシングのためにも使用されています。

BCCとは

BPFでカーネルのトレーシングやフィルダリングなどを行うためのツール郡です。Python用のバインディングもあり、BPFを容易に扱うことができます。(実際にはC言語も書くことになりますが…)
インストール方法はこちらをご覧ください。

BCCを使用しない場合、BPFアセンブラを直接書いたり、ClangでC言語をBPFにコンパイルしたりすることでBPFを利用できます。(LLVM 3.7からLLVMのバックエンドにBPFが追加されています。)

トレース

BPFを利用すれば、下の図のようにカーネルの多くの部分にアタッチすることができます。

https://github.com/iovisor/bcc

今回はすでに用意されているbpf/tools/tcptracer.py を利用して、TCPのコネクションをトレースしていきます。以下のようにPythonのファイルを実行します。(-t はタイムスタンプを表示するためのオプションです。)

$ sudo ./tcptracer.py -t
Tracing TCP established connections. Ctrl-C to end.
TIME(s) T PID COMM IP SADDR DADDR SPORT DPORT
0.000 C 7672 curl 4 127.0.0.1 127.0.0.1 59394 16004
0.006 X 7672 curl 4 127.0.0.1 127.0.0.1 59394 16004
0.006 X 7405 http-nio-16004- 6 [::] [0:ffff:7f00:1::] 0 65535
2.061 C 7682 curl 4 10.202.210.1 172.217.161.68 60036 443
2.177 X 7682 curl 4 10.202.210.1 172.217.161.68 60036 443
2.948 A 9565 java 4 127.0.0.1 127.0.0.1 42641 55980

プロセスごとのTCPコネクションの状態を確認することができました。T列のC,A,Xはそれぞれはシステムコールのconnect(), accept(), close()を示しています。

まとめ

BPFはプログラマブルでかつ、Linuxのさまざまな箇所にアタッチすることができるため、カーネルの動作を柔軟にトレースできます。また、BPFを容易に扱う手段としてBCCがあります。

参考

Software engineer