BSD界隈四方山話

第87回DTraceの使い方 その7

ネットワーク

今回はDTraceを使ってネットワーク関係のデータを集計するサンプルを紹介します。たとえば、次のようにdtrace(8)を実行すると受信したパケットを送信元のIPアドレスごとに集計して表示してくれます。

 受信したIPパケットを送信元アドレス毎に集計
# dtrace -n 'ip:::receive{@[args[2]->ip_saddr]=count()}'
dtrace: description 'ip:::receive' matched 1 probe
^C

  192.168.185.2                                                     4
  216.58.197.228                                                    5
  172.217.27.67                                                    13
  96.47.72.132                                                     53
  192.168.185.1                                                   612
#

先ほどのサンプルはプローブip:::receiveを指定してデータを集計していますが、これをip:::sendにして、今度は送信先アドレスごとにパケットのデータ長のデータを集めてグラフにまとめると次のような結果が得られます。

 送信パケットデータ長の分散グラフ
# dtrace -n 'ip:::send{@[args[2]->ip_daddr]=quantize(args[2]->ip_plength)}'
dtrace: description 'ip:::send' matched 1 probe
^C

  216.58.220.227
           value  ------------- Distribution ------------- count
               8 |                                         0
              16 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@          7
              32 |@@@@                                     1
              64 |@@@@                                     1
             128 |                                         0

  192.168.185.2
           value  ------------- Distribution ------------- count
              16 |                                         0
              32 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 11
              64 |                                         0

  216.58.197.227
           value  ------------- Distribution ------------- count
               8 |                                         0
              16 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@          14
              32 |@@@@                                     2
              64 |@@@@                                     2
             128 |                                         0

  96.47.72.132
           value  ------------- Distribution ------------- count
               8 |                                         0
              16 |@@@@@@@@@@@@@@@                          16
              32 |@@@@@@@@@@@@@@                           15
              64 |@@@@@@                                   6
             128 |                                         0
             256 |@@                                       2
             512 |@                                        1
            1024 |@@                                       2
            2048 |                                         0

  192.168.185.1
           value  ------------- Distribution ------------- count
              16 |                                         0
              32 |@                                        11
              64 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@      460
             128 |@@                                       31
             256 |                                         3
             512 |                                         4
            1024 |                                         5
            2048 |                                         3
            4096 |                                         3
            8192 |                                         0

#

これで目的地へのパケットのデータ長の様子がわかります。こんな感じでデータ長をチェックすると、たとえばジャンボフレームが使われることを想定している経路なのにジャンボフレームが使われていないとか、表示されるサイズを調べることでそういったことを検出できます。

今度はip:::receiveとip:::sendの双方を使ってやり取りの状況をモニタリングするスクリプトを取り上げます。

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

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

dtrace:::BEGIN
{
	printf(" %3s %10s %15s    %15s %8s %6s\n", "CPU", "DELTA(us)",
	    "SOURCE", "DEST", "INT", "BYTES");
	last = timestamp;
}

ip:::send
{
	this->delta = (timestamp - last) / 1000;
	printf(" %3d %10d %15s -> %15s %8s %6d\n", cpu, this->delta,
	    args[2]->ip_saddr, args[2]->ip_daddr, args[3]->if_name,
	    args[2]->ip_plength);
	last = timestamp;
}

ip:::receive
{
	this->delta = (timestamp - last) / 1000;
	printf(" %3d %10d %15s delta,
	    args[2]->ip_daddr, args[2]->ip_saddr, args[3]->if_name,
	    args[2]->ip_plength);
	last = timestamp;
}

これを実行すると次のような結果が得られます。こんな出力が簡単に得られるところがDTraceのすごいところです。

 ipio.dの実行結果
# ./ipio.d
 CPU  DELTA(us)          SOURCE               DEST      INT  BYTES
   2        674   192.168.1.101 ->    192.168.1.39     bge0    140
   1       4757   192.168.1.101     192.168.1.39     bge0    156
   2         13   192.168.1.101 ->    192.168.1.39     bge0    124
   1       2788   192.168.1.101     192.168.1.39     bge0    220
   2         13   192.168.1.101 ->    192.168.1.39     bge0    124
   1       4551   192.168.1.101     192.168.1.39     bge0    204
   2         12   192.168.1.101 ->    192.168.1.39     bge0    140
   1       1893   192.168.1.101     192.168.1.39     bge0    252
   2         13   192.168.1.101 ->    192.168.1.39     bge0     84
   1       3389   192.168.1.101     192.168.1.39     bge0    244
   2         12   192.168.1.101 ->    192.168.1.39     bge0    100
   1      44157   192.168.1.101     192.168.1.39     bge0    244
   3         12   192.168.1.101 ->    192.168.1.39     bge0    100
   1       2790   192.168.1.101     192.168.1.39     bge0    236
   3         13   192.168.1.101 ->    192.168.1.39     bge0    108
   1       3684   192.168.1.101     192.168.1.39     bge0    220
   3         12   192.168.1.101 ->    192.168.1.39     bge0    124
   1       2178   192.168.1.101 

同じくip:::receiveとip:::sendを使ってデータを集計して出力するスクリプトを紹介します。こちらは先ほどと違ってCtrl-Cが押されるまでのデータをため込んで、Ctrl-Cが押されると集計結果を出力します。

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

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

ip:::send,
ip:::receive
{
	this->protostr = args[2]->ip_ver == 4 ?
	    args[4]->ipv4_protostr : args[5]->ipv6_nextstr;
	@num[args[2]->ip_saddr, args[2]->ip_daddr, this->protostr] = count();
}

dtrace:::END
{
	printf("\n");
	printf("   %-28s %-28s %6s %8s\n", "SADDR", "DADDR", "PROTO", "COUNT");
	printa("   %-28s %-28s %6s %@8d\n", @num);
}

実行すると次のような結果が得られます。

 ipproto.dの実行結果
# ./ipproto.d
dtrace: script './ipproto.d' matched 4 probes
CPU     ID                    FUNCTION:NAME
  2      1                           :BEGIN Tracing... Hit Ctrl-C to end.

^C
  1      2                             :END
   SADDR                        DADDR                         PROTO    COUNT
   192.168.1.10                 192.168.1.101                   TCP        1
   192.168.1.101                192.168.1.10                    TCP        1
   192.168.1.101                210.173.160.57                  UDP        1
   0.0.0.0                      255.255.255.255                 UDP        2
   192.168.1.101                202.216.224.66                  UDP        3
   192.168.1.39                 255.255.255.255                 UDP        4
   192.168.1.53                 224.0.0.22                     IGMP        4
   192.168.1.101                202.216.246.91                  UDP        5
   202.216.246.91               192.168.1.101                   UDP        5
   192.168.1.107                239.255.255.250                 UDP        6
   192.168.1.106                239.255.255.250                 UDP        8
   192.168.1.39                 192.168.1.255                   UDP        8
   192.168.1.39                 239.255.255.250                 UDP        8
   192.168.1.107                255.255.255.255                 UDP       18
   192.168.1.10                 192.168.1.255                   UDP       26
   192.168.1.101                96.47.72.132                    TCP       27
   96.47.72.132                 192.168.1.101                   TCP       28
   192.168.1.53                 224.0.0.251                     UDP       32
   192.168.1.101                192.168.1.39                    TCP       83
   192.168.1.39                 192.168.1.101                   TCP       87


#

従来から提供されている*stat系のコマンドでは特定の通信に関して詳細なデータを取得するといったことを行うのが難しい面がありましたが、DTraceを使うとそういったことを簡単に実現できます。

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

勉強会

第60回 2月23日(木)19:00~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時間ほどの発表資料を作成していただき発表をお願いできればと思います。

おすすめ記事

記事・ニュース一覧