BSD界隈四方山話

第88回DTraceの使い方 その8

ネットワーク2

DTraceを使うとネットワーク通信に関するデータを簡単にモニタリングしたり集計して表示させることができます。コマンドを使っても似たようなデータは得られますが、目的に応じたデータをピンポイントで得られるところがDTraceのおもしろいところです。

まず簡単なサンプルから見てみましょう。次のDTraceスクリプトはtcp:::accept-establishedをモニタリングして、接続元ホストのIPアドレス、接続先ポート番号、接続回数(accept-established回数)をカウントするものです。

リスト tcpaccept.d
#!/usr/sbin/dtrace -s

#pragma D option quiet

dtrace:::BEGIN
{
    printf("Tracing... Hit Ctrl-C to end.\n");
}

tcp:::accept-established
{
    @num[args[2]->ip_saddr, args[4]->tcp_dport] = count();
}

dtrace:::END
{
    printf("   %-26s %-8s %8s\n", "HOST", "PORT", "COUNT");
    printa("   %-26s %-8d %@8d\n", @num);
}

実行すると次のような結果が得られます。ファイアウォールやinetdを使って似たようなデータを取得することもできますが、DTraceだとご覧のとおりです。シンプルなスクリプトでこうしたデータが得られます。

 tcpaccept.dの実行例
# ./tcpaccept.d
Tracing... Hit Ctrl-C to end.
^C
   HOST                       PORT        COUNT
   192.168.1.101              22              1
   192.168.1.39               22              1
   192.168.1.101              2049            2

#

似たようスクリプトで対象をtcp:::accept-establishedではなくtcp:::connect-establishedを使っても似たようなデータを取得できます(connect-established回数⁠⁠。

リスト tcpconnect.d
#!/usr/sbin/dtrace -s

#pragma D option quiet

dtrace:::BEGIN
{
    printf("Tracing... Hit Ctrl-C to end.\n");
}

tcp:::connect-established
{
    @num[args[2]->ip_daddr, args[4]->tcp_dport] = count();
}

dtrace:::END
{
    printf("   %-26s %-8s %8s\n", "HOST", "PORT", "COUNT");
    printa("   %-26s %-8d %@8d\n", @num);
}
 tcpconnect.dの実行例
# ./tcpconnect.d
Tracing... Hit Ctrl-C to end.
^C
   HOST                       PORT        COUNT
   192.168.1.10               57507           1

#

tcp:::sendとtcp:::receiveをモニタリングして送信元IP、送信先IP、それぞれのポート番号、バイト数、使われたCPU、TCPハンドシェークの様子を表示させることもできます。スクリプトは次のような感じになります。

リスト tcpio.d
#!/usr/sbin/dtrace -s

#pragma D option quiet
#pragma D option switchrate=10hz

dtrace:::BEGIN
{
    printf("%-3s %15s:%-5s      %15s:%-5s %6s %s\n", "CPU",
        "LADDR", "LPORT", "RADDR", "RPORT", "BYTES", "FLAGS");
}

tcp:::send
{
    this->length = args[2]->ip_plength - args[4]->tcp_offset;
    printf("%-3d %15s:%-5d  ->  %15s:%-5d %6d (", cpu,
        args[2]->ip_saddr, args[4]->tcp_sport,
        args[2]->ip_daddr, args[4]->tcp_dport, this->length);
}

tcp:::receive
{
    this->length = args[2]->ip_plength - args[4]->tcp_offset;
    printf("%-3d %15s:%-5d  <-  %15s:%-5d %6d (", cpu,
        args[2]->ip_daddr, args[4]->tcp_dport,
        args[2]->ip_saddr, args[4]->tcp_sport, this->length);
}

tcp:::send,
tcp:::receive
{
    printf("%s", args[4]->tcp_flags & TH_FIN ? "FIN|" : "");
    printf("%s", args[4]->tcp_flags & TH_SYN ? "SYN|" : "");
    printf("%s", args[4]->tcp_flags & TH_RST ? "RST|" : "");
    printf("%s", args[4]->tcp_flags & TH_PUSH ? "PUSH|" : "");
    printf("%s", args[4]->tcp_flags & TH_ACK ? "ACK|" : "");
    printf("%s", args[4]->tcp_flags & TH_URG ? "URG|" : "");
    printf("%s", args[4]->tcp_flags & TH_ECE ? "ECE|" : "");
    printf("%s", args[4]->tcp_flags & TH_CWR ? "CWR|" : "");
    printf("%s", args[4]->tcp_flags == 0 ? "null " : "");
    printf("\b)\n");
}

実行すると次のような感じです。

 tcpio.dの実行例
# ./tcpio.d
CPU           LADDR:LPORT                RADDR:RPORT  BYTES FLAGS
0    192.168.185.50:80     <-    192.168.185.1:60622     30 (FIN|ACK)
0    192.168.185.50:80     ->    192.168.185.1:60622     30 (ACK)
0    192.168.185.50:80     ->    192.168.185.1:60622     30 (FIN|ACK)
0    192.168.185.50:80     <-    192.168.185.1:60622     30 (ACK)
0    192.168.185.50:22     <-    192.168.185.1:52416     82 (PUSH|ACK)
0    192.168.185.50:22     ->    192.168.185.1:52416     58 (PUSH|ACK)
0    192.168.185.50:22     <-    192.168.185.1:52416     30 (ACK)
0    192.168.185.50:80     <-    192.168.185.1:60621    575 (PUSH|ACK)
0    192.168.185.50:80     ->    192.168.185.1:60621    210 (PUSH|ACK)
0    192.168.185.50:80     <-    192.168.185.1:60621     30 (ACK)
^C
#

特定の通信に絞り込めばTCPハンドシェークがどのように進むのかの概要も見ることができます。ネットワーク通信の中身を感じ取る方法としてはなかなか手軽で公開的な方法ではないかと思います。

ここに掲載したサンプルスクリプトは"DTrace Dynamic Tracing In Oracle Solaris, Mac OS X & FreeBSD", by Brendan Gregg and Jim Mauro P.487、P.489、P.491より抜粋したものです(一部内容を変更してあるほか、FreeBSDで実行した結果を掲載しています⁠⁠。

勉強会

第61回 3月23日(木)19:00~FreeBSD勉強会:リキャップ・ザ・AsiaBSDCon 2017 ~日本語でふりかえるABC~

2017年3月9~12日まで東京でAsiaBSDCon 2017が開催される予定です。ぜひこのカンファレンスにご参加いただきたいわけなのですが、なかにはどうしても仕事の都合で参加できなかったとか、正直英語がよくわからなかったとか、そういった方もいらっしゃるのではないかと思います。

3月のFreeBSD勉強会では、AsiaBSDCon 2017のあとというこのタイミングを活かして、AsiaBSDCon 2017の発表内容を振り返ってみよう、というのをやってみようと思います。AsiaBSDConに参加しているにもかかわらず、これまで一度もプロシーディングを読み返したことすらないというあなた、ぜひプロシーディングを持参してご参加ください :) AsiaBSDConに参加できなかったというあなたも、この機会をお見逃しなく(できればAsiaBSDConそのものに参加したほうが絶対的によいです、あしからず⁠⁠。

FreeBSD勉強会 発表者募集

FreeBSD勉強会では発表者を募集しています。FreeBSDに関して発表を行いたい場合、@daichigoto までメッセージをお願いします。1時間半〜2時間ほどの発表資料を作成していただき発表をお願いできればと思います。

おすすめ記事

記事・ニュース一覧