ネットワーク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ハンドシェークがどのように進むのかの概要も見ることができます。ネットワーク通信の中身を感じ取る方法としてはなかなか手軽で公開的な方法ではないかと思います。
勉強会
第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時間ほどの発表資料を作成していただき発表をお願いできればと思います。