ネットワークを調査する 50 の方法
By Daisuke Homma on 4 20, 2009
はじめに
今回は Solaris でネットワークの性能や障害を調査するコマンドをご紹介したいと思います。私が普段よく使用する物を中心に、用途別に大体 50 種類の手順をまとめました。
ネットワークの解析方法
ネットワークインターフェイスの一覧を表示する
ifconfig -a
現在使用可能なネットワークインターフェイスの一覧は "ifconfig -a" で調べる事が出来ます。インターフェイス名、MTU、IP アドレス、ネットマスク等の情報も得る事が出来ます。
% ifconfig -a lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1 inet 127.0.0.1 netmask ff000000 e1000g0: flags=201000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4,CoS> mtu 1500 index 2 inet 10.16.67.5 netmask ffff0000 broadcast 10.16.255.255
root ユーザで実行した場合は MAC アドレスも表示されます。
# ifconfig -a lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1 inet 127.0.0.1 netmask ff000000 e1000g0: flags=201000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4,CoS> mtu 1500 index 2 inet 10.16.67.5 netmask ffff0000 broadcast 10.16.255.255 ether 0:23:8b:64:88:60
"ifconfig -a plumb" で全てのインターフェイスを有効にしてから "ifconfig -a" を実行すると、システムで利用可能な全てのインターフェイスを表示する事が出来ます。
# ifconfig -a plumb ifconfig: SIOCSLIFNAME for ip: e1000g0: already exists # ifconfig -a lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1 inet 127.0.0.1 netmask ff000000 e1000g0: flags=201000843<UP,BROADCAST,RUNNING,MULTICAST,IPv4,CoS> mtu 1500 index 2 inet 10.16.67.5 netmask ffff0000 broadcast 10.16.255.255 ether 0:23:8b:64:88:60 e1000g1: flags=201000842<BROADCAST,RUNNING,MULTICAST,IPv4,CoS> mtu 1500 index 3 inet 0.0.0.0 netmask 0 ether 0:23:8b:64:88:61 e1000g2: flags=201000842<BROADCAST,RUNNING,MULTICAST,IPv4,CoS> mtu 1500 index 4 inet 0.0.0.0 netmask 0 ether 0:23:8b:64:88:62 e1000g3: flags=201000842<BROADCAST,RUNNING,MULTICAST,IPv4,CoS> mtu 1500 index 5 inet 0.0.0.0 netmask 0 ether 0:23:8b:64:88:63
dladm show-dev
"dladm show-dev" コマンドでもインターフェイスの一覧を表示する事が出来ます。リンクの状態やリンクスピード、デュプレックスの情報も入手出来ます。また、有効になっていないインターフェイスについても見る事が出来ます。dladm コマンドは root ユーザで実行する必要があります。
# dladm show-dev e1000g0 link: up speed: 100 Mbps duplex: full e1000g1 link: down speed: 0 Mbps duplex: half e1000g2 link: down speed: 0 Mbps duplex: half e1000g3 link: down speed: 0 Mbps duplex: half
dladm show-link
"dladm show-link" をすると、リンクタイプや MTU を表示する事が出来ます。
# dladm show-link e1000g0 type: non-vlan mtu: 1500 device: e1000g0 e1000g1 type: non-vlan mtu: 1500 device: e1000g1 e1000g2 type: non-vlan mtu: 1500 device: e1000g2 e1000g3 type: non-vlan mtu: 1500 device: e1000g3
インターフェイス名のみを取得するスクリプト
シェルスクリプトで NIC の名前だけをリストにして抜き出せると便利です。以下の一行を実行すると ${NIC} にリンクアップしているインターフェイスの名前がスペース区切りで格納されます。
# NIC=`dladm show-dev -p | grep 'link=up' | awk '{printf "%s ", $1}'`
これはスクリプトの中で使うと便利です。以下のスクリプトでは各インターフェイスについて "dladm show-dev -s -i 1 <NIC>" を実行して統計情報をログに保存しています。
#!/bin/sh NIC=`dladm show-dev -p | grep 'link=up' | awk '{printf "%s ", $1}'` for i in ${NIC} do dladm show-dev -s -i 1 ${i} > dladm-${i}.log & done
インターフェイスの状態を調べる
インターフェイスがリンクアップしているかどうかは "kstat -p ':::link_up'" でも調べられます。1 がリンクアップです。
# kstat -p ':::link_up' e1000g:0:mac:link_up 1 e1000g:1:mac:link_up 0 e1000g:2:mac:link_up 0 e1000g:3:mac:link_up 0
インターフェイスのリンクスピードは "kstat -p ':::link_speed'" でも調べられます。単位は Mbps です。
# kstat -p ':::link_speed' e1000g:0:statistics:link_speed 100 e1000g:1:statistics:link_speed 0 e1000g:2:statistics:link_speed 0 e1000g:3:statistics:link_speed 0
インターフェイスのデュプレックスは "kstat -p ':::duplex'" でも調べられます。2 が full duplex です。
# kstat -p ':::link_duplex' e1000g:0:mac:link_duplex 2 e1000g:1:mac:link_duplex 1 e1000g:2:mac:link_duplex 1 e1000g:3:mac:link_duplex 1
ネットワークセッションの一覧を表示する
netstat -a
"netstat -a" コマンドを使用すると現在のソケットの状態を調べる事が出来ます。State が "ESTABLISHED" になっているソケットはセッションが確立している接続です。
# netstat -a ... TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State -------------------- -------------------- ----- ------ ----- ------ ----------- \*.\* \*.\* 0 0 49152 0 IDLE \*.sunrpc \*.\* 0 0 49152 0 LISTEN \*.\* \*.\* 0 0 49152 0 IDLE \*.32771 \*.\* 0 0 49152 0 LISTEN \*.32772 \*.\* 0 0 49152 0 LISTEN ... sanuki05.5001 10.16.62.7.35163 49640 0 49640 0 ESTABLISHED sanuki05.5001 10.16.62.7.35164 49640 0 49640 0 ESTABLISHED sanuki05.5001 10.16.62.7.35165 49640 0 49640 0 ESTABLISHED sanuki05.5001 10.16.62.7.35166 49640 0 49640 0 ESTABLISHED sanuki05.5001 10.16.62.7.35167 49640 0 49640 0 ESTABLISHED
"netstat -aP tcp -f inet" を実行すると、IPv4 で TCP を使用しているソケットだけを抜き出す事が出来ます。
# netstat -aP tcp -f inet TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State -------------------- -------------------- ----- ------ ----- ------ ----------- \*.\* \*.\* 0 0 49152 0 IDLE \*.sunrpc \*.\* 0 0 49152 0 LISTEN \*.\* \*.\* 0 0 49152 0 IDLE \*.32771 \*.\* 0 0 49152 0 LISTEN \*.32772 \*.\* 0 0 49152 0 LISTEN \*.lockd \*.\* 0 0 49152 0 LISTEN ...
セッション数を調べる
netstat -a | grep ESTAB | wc -l
"netstat -a | grep ESTAB | wc -l" を実行すると、現在繋がっているネットワークセッションの本数を調べる事が出来ます。以下の様に while 分で回せば、毎秒のセッション数の増減を記録する事が出来ます。以下の例では 34 本のセッションが張られています。
# while :; do netstat -a | grep ESTAB | wc -l; sleep 1; done 34 34 34
"netstat -a | grep telnet | grep ESTAB | wc -l" を実行すると、telnet のセッションだけを抜き出す事が出来ます。同じ様に while で回せば、telnet セッションの増減を記録する事が出来ます。telnet 以外にも http や 1521 番の Oracle のセッションだけを抜き出す事も可能です。以下の例では 8 本の telnet セッションが張られています。
# while :; do netstat -a | grep telnet | grep ESTAB | wc -l; sleep 1; done 8 8 8
kstat -p tcp:0:tcp:currEstab 1
"kstat -p tcp:0:tcp:currEstab 1" を実行すると TCP のセッション数を出力します。この値は /usr/src/uts/common/inet/tcp/tcp.c の tcp_kstat_update() 関数で集計されています。
% kstat -p tcp:0:tcp:currEstab 1 tcp:0:tcp:currEstab 4 tcp:0:tcp:currEstab 4 tcp:0:tcp:currEstab 3
スループットを調べる
dladm show-dev -s -i 1 <NIC>
"dladm show-dev -s -i 1 <NIC>" を実行すると、受信したパケット数(ipackets)、受信したデータのバイト数(rbytes)、受信の際に発生したエラーの数(ierrors)、送信したパケット数(opackets)、送信したデータのバイト数(obytes)、送信の際に発生したエラーの数(oerrors)を 1 秒毎に表示する事が出来ます。最初の出力はブートしてからの累積値です。
# dladm show-dev -s -i 1 e1000g0 ipackets rbytes ierrors opackets obytes oerrors e1000g0 4048080 2168050372 0 611314 120697887 0 ipackets rbytes ierrors opackets obytes oerrors e1000g0 5 320 0 2 366 0 ipackets rbytes ierrors opackets obytes oerrors e1000g0 6 384 0 2 280 0
netstat -i -I <NIC> 1
"netstat -i -I <NIC> 1" でもネットワークのスループットを表示する事が出来ますが、送受信バイト数を見る事は出来ません。出来る限り dladm コマンドをお使い下さい。
# netstat -i -I e1000g0 1 input e1000g output input (Total) output packets errs packets errs colls packets errs packets errs colls 4048518 0 611396 0 0 4048688 0 611566 0 0 3 0 1 0 0 3 0 1 0 0 4 0 2 0 0 4 0 2 0 0
kstat -p '<NIC>:::\*bytes' 1
"kstat -p '<NIC>:::\*bytes' 1" を実行すると送受信バイト数の累積を表示する事が出来ます。これが dladm や netstat の出力の元データです。kstat の引数はインターフェイス毎に多少異なります。kstat のデータは殆ど整形されていない生データなので加工してお使い下さい。
# kstat -p 'e1000g:0:e1000g0:\*bytes' 1 e1000g:0:e1000g0:obytes 120717403 e1000g:0:e1000g0:rbytes 2168116829 e1000g:0:e1000g0:obytes 120717532 e1000g:0:e1000g0:rbytes 2168117213 e1000g:0:e1000g0:obytes 120717789 e1000g:0:e1000g0:rbytes 2168117597
"kstat -p '<NIC>:::\*packets' 1" を実行すると送受信パケット数の累積を表示する事が出来ます。kstat には他にも便利な統計情報が入っていますので、是非お試し下さい。
# kstat -p 'e1000g:0:e1000g0:\*packets' 1 e1000g:0:e1000g0:ipackets 4050305 e1000g:0:e1000g0:opackets 611558 e1000g:0:e1000g0:ipackets 4050309 e1000g:0:e1000g0:opackets 611559 e1000g:0:e1000g0:ipackets 4050315 e1000g:0:e1000g0:opackets 611561
ネットワークインターフェイスの CPU 使用率を見る
intrstat 1
"intrstat 1" を実行するとデバイスが発生させたインタラプトの処理に使用している CPU の割合を調べる事が出来ます。以下の例では e1000g0 インターフェイスが CPU 6 番を 5.0% 使用している事が分かります。
# intrstat 1 ... device | cpu0 %tim cpu1 %tim cpu2 %tim cpu3 %tim -------------+------------------------------------------------------------ ahci#0 | 0 0.0 0 0.0 0 0.0 0 0.0 e1000g#0 | 0 0.0 0 0.0 0 0.0 0 0.0 e1000g#2 | 0 0.0 0 0.0 0 0.0 0 0.0 ehci#0 | 0 0.0 0 0.0 0 0.0 0 0.0 uhci#0 | 0 0.0 0 0.0 0 0.0 0 0.0 uhci#1 | 0 0.0 0 0.0 0 0.0 0 0.0 uhci#2 | 0 0.0 0 0.0 0 0.0 0 0.0 uhci#3 | 0 0.0 0 0.0 0 0.0 0 0.0 device | cpu4 %tim cpu5 %tim cpu6 %tim cpu7 %tim -------------+------------------------------------------------------------ ahci#0 | 0 0.0 2 0.0 0 0.0 0 0.0 e1000g#0 | 0 0.0 0 0.0 8204 5.0 0 0.0 e1000g#2 | 0 0.0 0 0.0 1 0.0 0 0.0 ehci#0 | 18 0.0 0 0.0 0 0.0 0 0.0 uhci#0 | 18 0.0 0 0.0 0 0.0 0 0.0 uhci#1 | 0 0.0 1 0.0 0 0.0 0 0.0 uhci#2 | 0 0.0 1 0.0 0 0.0 0 0.0 uhci#3 | 0 0.0 2 0.0 0 0.0 0 0.0
ネットワークインターフェイスの統計情報を見る
kstat -p '<NIC>:::' 1
"kstat -p '<NIC>:<Instance>::' 1" を実行すると、カーネルに保存してあるインターフェイスの情報を表示する事が出来ます。kstat には性能に関する情報を含め、様々なデータが記録されています。<Instance> は ifconfig コマンド等で表示される NIC のインスタンス番号です。以下の例では e1000g0 インターフェイスの統計情報を表示しています。出力はそれぞれ重要な意味を持っています。例えば norcvbuf が発生している場合は受信用のバッファが不足している事を意味しています。
# kstat -p 'e1000g:0::' e1000g:0:e1000g0:brdcstrcv 2621877 e1000g:0:e1000g0:brdcstxmt 4542 e1000g:0:e1000g0:class net e1000g:0:e1000g0:collisions 0 e1000g:0:e1000g0:crtime 135.093656493 e1000g:0:e1000g0:ierrors 0 e1000g:0:e1000g0:ifspeed 100000000 e1000g:0:e1000g0:ipackets 4533601 e1000g:0:e1000g0:ipackets64 4533601 e1000g:0:e1000g0:multircv 21570 e1000g:0:e1000g0:multixmt 0 e1000g:0:e1000g0:norcvbuf 0 e1000g:0:e1000g0:noxmtbuf 0 e1000g:0:e1000g0:obytes 135842713 e1000g:0:e1000g0:obytes64 135842713 e1000g:0:e1000g0:oerrors 0 e1000g:0:e1000g0:opackets 846546 e1000g:0:e1000g0:opackets64 846546 e1000g:0:e1000g0:rbytes 2884822219 e1000g:0:e1000g0:rbytes64 2884822219 ...
nxge の DMA Channel の統計情報を取得する
nxge インターフェイスは複数の転送経路にデータを分散する事で高いスループットを達成しています。データの分散状況は kstat コマンドを使用して調べる事が出来ます。kstat の出力の各行が転送経路です。一番右側の数値がその経路を通って転送されたパケット数の累積値です。値の増加に偏りがある場合はインターフェイスの設定を見直して下さい。
# kstat -p 'nxge:1::\*dc_packets' 1 nxge:1:RDC Channel 0 Stats:rdc_packets 17920164 nxge:1:RDC Channel 1 Stats:rdc_packets 17876325 nxge:1:RDC Channel 2 Stats:rdc_packets 18551372 nxge:1:RDC Channel 3 Stats:rdc_packets 17262833 nxge:1:RDC Channel 4 Stats:rdc_packets 17916873 nxge:1:RDC Channel 5 Stats:rdc_packets 18427140 nxge:1:RDC Channel 6 Stats:rdc_packets 18380673 nxge:1:RDC Channel 7 Stats:rdc_packets 18262768 nxge:1:TDC Channel 0 Stats:tdc_packets 25398140 nxge:1:TDC Channel 1 Stats:tdc_packets 26289370 nxge:1:TDC Channel 2 Stats:tdc_packets 25947133 nxge:1:TDC Channel 3 Stats:tdc_packets 25353460 nxge:1:TDC Channel 4 Stats:tdc_packets 27044560 nxge:1:TDC Channel 5 Stats:tdc_packets 26080875 nxge:1:TDC Channel 6 Stats:tdc_packets 25467176 nxge:1:TDC Channel 7 Stats:tdc_packets 25782669 ...
ネットワークインターフェイスのパラメータを調べる
ndd -get /dev/<NIC>
"ndd -get /dev/<NIC> \\?" を実行するとインターフェイスのパラメータ一の一部を取得する事が出来ます。
# ndd -get /dev/e1000g0 \\? ? (read only) autoneg_cap (read only) pause_cap (read only) asym_pause_cap (read only) 1000fdx_cap (read only) 1000hdx_cap (read only) 100T4_cap (read only) 100fdx_cap (read only) 100hdx_cap (read only) 10fdx_cap (read only) 10hdx_cap (read only) adv_autoneg_cap (read and write) ...
パラメータの値を取得する場合はパラメータ名を指定します。"(read and write)" と表示されているパラメータは -set オプションを使用して値を設定する事も可能です。
# ndd -get /dev/e1000g0 max_num_rcv_packets 128
TCP の統計情報を見る
netstat -sP tcp 1
"netstat -sP tcp 1" を実行すると TCP の統計情報を表示する事が出来ます。もし tcpListenDrop が発生していたら要対応です。
# netstat -sP tcp 1 ... TCP tcpRtoAlgorithm = 0 tcpRtoMin = 400 tcpRtoMax = 60000 tcpMaxConn = -1 tcpActiveOpens = 0 tcpPassiveOpens = 0 tcpAttemptFails = 0 tcpEstabResets = 0 tcpCurrEstab = 23 tcpOutSegs = 20 tcpOutDataSegs = 16 tcpOutDataBytes = 5568 tcpRetransSegs = 0 tcpRetransBytes = 0 tcpOutAck = 4 tcpOutAckDelayed = 2 tcpOutUrg = 0 tcpOutWinUpdate = 0 tcpOutWinProbe = 0 tcpOutControl = 0 tcpOutRsts = 0 tcpOutFastRetrans = 0 tcpInSegs = 15 tcpInAckSegs = 9 tcpInAckBytes = 5568 tcpInDupAck = 0 tcpInAckUnsent = 0 tcpInInorderSegs = 6 tcpInInorderBytes = 3122 tcpInUnorderSegs = 0 tcpInUnorderBytes = 0 tcpInDupSegs = 0 tcpInDupBytes = 0 tcpInPartDupSegs = 0 tcpInPartDupBytes = 0 tcpInPastWinSegs = 0 tcpInPastWinBytes = 0 tcpInWinProbe = 0 tcpInWinUpdate = 0 tcpInClosed = 0 tcpRttNoUpdate = 0 tcpRttUpdate = 9 tcpTimRetrans = 0 tcpTimRetransDrop = 0 tcpTimKeepalive = 0 tcpTimKeepaliveProbe= 0 tcpTimKeepaliveDrop = 0 tcpListenDrop = 0 tcpListenDropQ0 = 0 tcpHalfOpenDrop = 0 tcpOutSackRetrans = 0
kstat -p 'tcp:0::'
"kstat -p 'tcp:0::' 1" を実行すると TCP に関する統計情報を出力する事が出来ます。このコマンドで取得出来るデータが "netstat -sP tcp 1" の元になっています。
% kstat -p 'tcp:0::' 1 tcp:0:tcp:activeOpens 67462 tcp:0:tcp:attemptFails 445 tcp:0:tcp:class mib2 tcp:0:tcp:connTableSize 72 tcp:0:tcp:connTableSize6 96 tcp:0:tcp:crtime 105.965466 tcp:0:tcp:currEstab 23 tcp:0:tcp:estabResets 56804 ...
tcpstat.d
DTrace Toolkit には tcpstat.d という TCP の送受信量をカウントするスクリプトが入っています。
# ./tcpstat.d TCP_out TCP_outRe TCP_in TCP_inDup TCP_inUn 0 0 0 0 0 122 0 0 0 0 61 0 0 0 0 61 0 0 0 0 314670 0 314609 0 0 159488 0 159427 0 0 31213 0 31152 0 0 ...
ndd -get /dev/tcp
"ndd -get /dev/tcp \\?" を実行すると TCP のパラメータの一部を表示する事が出来ます。
# ndd -get /dev/tcp \\? ? (read only) tcp_time_wait_interval (read and write) tcp_conn_req_max_q (read and write) tcp_conn_req_max_q0 (read and write) tcp_conn_req_min (read and write) tcp_conn_grace_period (read and write) tcp_cwnd_max (read and write) tcp_debug (read and write) tcp_smallest_nonpriv_port (read and write) ...
パラメータ名を指定するとパラメータの値を取得する事が出来ます。"(read and write)" なパラメータに関しては -set を指定すると値を設定する事が出来ます。
# ndd -get /dev/tcp tcp_time_wait_interval 60000
IP の統計情報を見る
netstat -sP ip 1
"netstat -sP ip 1" を実行すると IP の統計情報を表示する事が出来ます。
% netstat -sP ip 1 ... IPv4 ipForwarding = 2 ipDefaultTTL = 255 ipInReceives = 2 ipInHdrErrors = 0 ipInAddrErrors = 0 ipInCksumErrs = 0 ipForwDatagrams = 0 ipForwProhibits = 0 ipInUnknownProtos = 0 ipInDiscards = 0 ipInDelivers = 2 ipOutRequests = 2 ipOutDiscards = 0 ipOutNoRoutes = 0 ipReasmTimeout = 0 ipReasmReqds = 0 ipReasmOKs = 0 ipReasmFails = 0 ipReasmDuplicates = 0 ipReasmPartDups = 0 ipFragOKs = 0 ipFragFails = 0 ipFragCreates = 0 ipRoutingDiscards = 0 tcpInErrs = 0 udpNoPorts = 0 udpInCksumErrs = 0 udpInOverflows = 0 rawipInOverflows = 0 ipsecInSucceeded = 0 ipsecInFailed = 0 ipInIPv6 = 0 ipOutIPv6 = 0 ipOutSwitchIPv6 = 0 ...
同様に ICMP や RAWIP の統計情報を表示させる事も可能です。
% netstat -sP icmp 1 ICMPv4 icmpInMsgs = 0 icmpInErrors = 0 icmpInCksumErrs = 0 icmpInUnknowns = 0 icmpInDestUnreachs = 0 icmpInTimeExcds = 0 icmpInParmProbs = 0 icmpInSrcQuenchs = 0 icmpInRedirects = 0 icmpInBadRedirects = 0 icmpInEchos = 0 icmpInEchoReps = 0 icmpInTimestamps = 0 icmpInTimestampReps = 0 icmpInAddrMasks = 0 icmpInAddrMaskReps = 0 icmpInFragNeeded = 0 icmpOutMsgs = 0 icmpOutDrops = 0 icmpOutErrors = 0 icmpOutDestUnreachs = 0 icmpOutTimeExcds = 0 icmpOutParmProbs = 0 icmpOutSrcQuenchs = 0 icmpOutRedirects = 0 icmpOutEchos = 0 icmpOutEchoReps = 0 icmpOutTimestamps = 0 icmpOutTimestampReps= 0 icmpOutAddrMasks = 0 icmpOutAddrMaskReps = 0 icmpOutFragNeeded = 0 icmpInOverflows = 0
kstat -p 'ip:::' 1
"kstat -p 'ip:::' 1" を実行すると、カーネル内に保存されている IP に関する統計情報を出力する事が出来ます。これが "netstat -sP ip 1" の出力の元データです。
% kstat -p 'ip:::' 1 ... ip:0:ip:inDelivers 4629963 ip:0:ip:inDiscards 0 ip:0:ip:inErrs 0 ip:0:ip:inHdrErrors 0 ip:0:ip:inIPv6 0 ip:0:ip:inReceives 4862389 ip:0:ip:inUnknownProtos 21643 ...
ndd -get /dev/ip
"ndd -get /dev/ip \\?" を実行すると IP のパラメータの一部を取得する事が出来ます。
# ndd -get /dev/ip \\? ? (read only) ip_respond_to_address_mask_broadcast(read and write) ip_respond_to_echo_broadcast (read and write) ip_respond_to_echo_multicast (read and write) ip_respond_to_timestamp (read and write) ip_respond_to_timestamp_broadcast(read and write) ip_send_redirects (read and write)
パラメータ名を指定すると値を取得する事が出来ます。"(read and write)" と表示されているパラメータは -set オプションで値を設定する事も可能です。
# ndd -get /dev/ip ip_lso_outbound 1
どんなパケットが流れているかを調べる
snoop -d <NIC>
"snoop -d <NIC>" を実行すると、送受信したパケット毎に送信元 IP アドレス、宛先 IP アドレス、プロトコル、ポート番号等を調べる事が出来ます。他の OS の tcpdump に相当するコマンドです。"TELNET" と表示されているパケットは telnet のパケットです。-r オプションは名前解決をさせない為に付けていますが、必ずしも付ける必要はありません。snoop の実行はそれ自体がネットワークの負荷になりますので、必要な時にだけ使用して下さい。
# snoop -r -d e1000g0 Using device /dev/e1000g0 (promiscuous mode) 10.16.100.16 -> 10.16.67.5 TELNET C port=40383 10.16.67.5 -> 10.16.100.16 TELNET R port=40383 Using device /dev/e1 10.16.100.16 -> 10.16.67.5 TELNET C port=40383 10.16.11.16 -> (broadcast) ARP C Who is 10.16.0.200, 10.16.0.200 ? 10.16.64.5 -> (broadcast) ARP C Who is 10.16.201.102, 10.16.201.102 ? ? -> \* ETHER Type=9000 (Loopback), size = 60 bytes
パケットサイズを調べる
snoop -d <NIC> -S
"snoop -d <NIC> -S" を実行すると length: に続いてパケットのサイズを出力させる事が可能です。パケットのサイズによってネットワークの負荷が変わってきます(一般にパケットが小さい方が負荷が高い)。
# snoop -d e1000g0 -S 10.16.98.12 -> (broadcast) length: 60 ARP C Who is 10.16.38.2, 10.16.38.2 ? ? -> (multicast) length: 52 ETHER Type=0000 (LLC/802.3), size = 52 bytes 10.16.38.2 -> 10.255.255.255 length: 178 BPARAM C GETFILE root 10.16.64.5 -> (broadcast) length: 60 ARP C Who is 10.16.64.54, 10.16.64.54 ? 10.16.98.16 -> (broadcast) length: 60 ARP C Who is 10.16.38.2, 10.16.38.2 ?
詳しくパケットの中身を調べる
snoop -d <NIC> -V
"snoop -r -d <NIC> -V" を実行するとネットワークの階層毎に詳細な情報を出力させる事が出来ます。"________________________________" で挟まれている部分が一つのパケットです。"ETHER"、"IP"、"TCP"、"TELNET" と階層毎に一行出力されているのが分かります。
# snoop -r -d e1000g0 -V Using device /dev/e1000g0 (promiscuous mode) ________________________________ 10.16.100.16 -> 10.16.67.5 ETHER Type=0800 (IP), size = 60 bytes 10.16.100.16 -> 10.16.67.5 IP D=10.16.67.5 S=10.16.100.16 LEN=40, ID=44170, TOS=0x0, TTL=64 10.16.100.16 -> 10.16.67.5 TCP D=23 S=40383 Ack=2979015144 Seq=1218855275 Len=0 Win=49640 10.16.100.16 -> 10.16.67.5 TELNET C port=40383 ________________________________ 10.16.67.5 -> 10.16.100.16 ETHER Type=0800 (IP), size = 100 bytes 10.16.67.5 -> 10.16.100.16 IP D=10.16.100.16 S=10.16.67.5 LEN=86, ID=8412, TOS=0x0, TTL=60 10.16.67.5 -> 10.16.100.16 TCP D=40383 S=23 Push Ack=1218855275 Seq=2979015144 Len=46 Win=49640 10.16.67.5 -> 10.16.100.16 TELNET R port=40383 Using device /dev/e1 ________________________________
snoop -d <NIC> -v
"snoop -d <NIC> -v" を実行すると更に詳しくパケットの中身を調べる事が出来ます。各ヘッダの中身とペイロードの中身が可能な限り分かりやすく整形されて出力されます。
# snoop -d <NIC> -v ... ETHER: ----- Ether Header ----- ETHER: ETHER: Packet 2999 arrived at 17:59:25.31440 ETHER: Packet size = 91 bytes ETHER: Destination = 0:3:ba:37:34:a8, ETHER: Source = 0:23:8b:64:88:60, ETHER: Ethertype = 0800 (IP) ETHER: IP: ----- IP Header ----- IP: IP: Version = 4 IP: Header length = 20 bytes IP: Type of service = 0x00 IP: xxx. .... = 0 (precedence) IP: ...0 .... = normal delay IP: .... 0... = normal throughput IP: .... .0.. = normal reliability IP: .... ..0. = not ECN capable transport IP: .... ...0 = no ECN congestion experienced IP: Total length = 77 bytes IP: Identification = 18244 IP: Flags = 0x4 IP: .1.. .... = do not fragment IP: ..0. .... = last fragment IP: Fragment offset = 0 bytes IP: Time to live = 60 seconds/hops IP: Protocol = 6 (TCP) IP: Header checksum = 0000 IP: Source address = 10.16.67.5, 10.16.67.5 IP: Destination address = 10.16.100.16, 10.16.100.16 IP: No options IP: TCP: ----- TCP Header ----- TCP: TCP: Source port = 23 TCP: Destination port = 40383 TCP: Sequence number = 2990317620 TCP: Acknowledgement number = 1218855394 TCP: Data offset = 20 bytes TCP: Flags = 0x18 TCP: 0... .... = No ECN congestion window reduced TCP: .0.. .... = No ECN echo TCP: ..0. .... = No urgent pointer TCP: ...1 .... = Acknowledgement TCP: .... 1... = Push TCP: .... .0.. = No reset TCP: .... ..0. = No Syn TCP: .... ...0 = No Fin TCP: Window = 49640 TCP: Checksum = 0xbb74 TCP: Urgent pointer = 0 TCP: No options TCP: TELNET: ----- TELNET: ----- TELNET: TELNET: "IP: ----- IP Header -----\\r\\nIP: \\r\\n" TELNET: ...
snoop -d <NIC> -x <OFFSET>
"snoop -d <NIC> -x <OFFSET>" を実行すると、パケットの中身をダンプする事が出来ます。通信の内容を調査したい時に使用して下さい。<OFFSET> をゼロにするとパケットのヘッダも含めて全てのデータをダンプする事が出来ます。以下は telnet ポートの通信をペイロードのみ表示させた例です。telnet で接続した状態で id コマンドを を発行して "uid=0(root) gid=0(root)" の出力を得ています。
# snoop -r -d e1000g0 -x 54 port 23 Using device /dev/e1000g0 (promiscuous mode) 10.16.100.16 -> 10.16.67.5 TELNET C port=40386 i 0: 6900 0000 0000 i..... 10.16.67.5 -> 10.16.100.16 TELNET R port=40386 i 0: 69 i 10.16.100.16 -> 10.16.67.5 TELNET C port=40386 0: 0000 0000 0000 ...... 10.16.100.16 -> 10.16.67.5 TELNET C port=40386 d 0: 6400 0000 0000 d..... 10.16.67.5 -> 10.16.100.16 TELNET R port=40386 d 0: 64 d 10.16.100.16 -> 10.16.67.5 TELNET C port=40386 0: 0000 0000 0000 ...... 10.16.100.16 -> 10.16.67.5 TELNET C port=40386 0: 0d00 0000 0000 ...... 10.16.67.5 -> 10.16.100.16 TELNET R port=40386 0: 0d0a .. 10.16.100.16 -> 10.16.67.5 TELNET C port=40386 0: 0000 0000 0000 ...... 10.16.67.5 -> 10.16.100.16 TELNET R port=40386 uid=0(root) gid=0(ro 0: 7569 643d 3028 726f 6f74 2920 6769 643d uid=0(root) gid= 16: 3028 726f 6f74 290d 0a23 20 0(root)..# 10.16.100.16 -> 10.16.67.5 TELNET C port=40386 0: 0000 0000 0000 ......
パケットをファイルにダンプする
snoop -o <FILE> -d <NIC>
"snoop -o <FILE> -d <NIC> を実行すると送受信したパケットを全てファイルに保存する事が出来ます。通信の内容を一旦ファイルに保存して後でゆっくり解析する事が出来るので非常に便利です。通信量に応じてファイルのサイズも大きくなりますので、ご注意下さい。
# snoop -o /var/tmp/snoop01.dump -d e1000g0 Using device /dev/e1000g0 (promiscuous mode) 24 \^C
保存したファイルは "snoop -i <FILE>" で読み込む事が出来ます。今までご紹介した snoop のオプションも合わせて使用する事が出来ます。
# snoop -r -i /var/tmp/snoop01.dump -V | head ________________________________ 1 0.00000 10.16.100.16 -> 10.16.67.5 ETHER Type=0800 (IP), size = 60 bytes 1 0.00000 10.16.100.16 -> 10.16.67.5 IP D=10.16.67.5 S=10.16.100.16 LEN=40, ID=21844, TOS=0x0, TTL=64 1 0.00000 10.16.100.16 -> 10.16.67.5 TCP D=23 S=40383 Ack=3027145344 Seq=1218855542 Len=0 Win=49640 1 0.00000 10.16.100.16 -> 10.16.67.5 TELNET C port=40383 ________________________________ 2 0.00001 10.16.67.5 -> 10.16.100.16 ETHER Type=0800 (IP), size = 104 bytes 2 0.00001 10.16.67.5 -> 10.16.100.16 IP D=10.16.100.16 S=10.16.67.5 LEN=90, ID=28393, TOS=0x0, TTL=60 2 0.00001 10.16.67.5 -> 10.16.100.16 TCP D=40383 S=23 Push Ack=1218855542 Seq=3027145344 Len=50 Win=49640 2 0.00001 10.16.67.5 -> 10.16.100.16 TELNET R port=40383 Using device /dev
別のホストに到達可能かを調べる
ping <HOST>
"ping <HOST>" を実行すると、指定したホストまで到達可能か確認する事が出来ます。
# ping 10.16.62.7 10.16.62.7 is alive
"ping -s <HOST>" を実行すると、定期的に ping を打つ事が出来ます。
# ping -s 10.16.62.7 PING 10.16.62.7: 56 data bytes 64 bytes from 10.16.62.7: icmp_seq=0. time=0.872 ms 64 bytes from 10.16.62.7: icmp_seq=1. time=0.280 ms 64 bytes from 10.16.62.7: icmp_seq=2. time=0.214 ms \^C ----10.16.62.7 PING Statistics---- 3 packets transmitted, 3 packets received, 0% packet loss round-trip (ms) min/avg/max/stddev = 0.214/0.455/0.872/0.36
ブロードキャストアドレス宛に ping を打つと同じセグメントに居るマシンを調べる事が出来ます。
# ping -s 10.16.255.255 PING 10.16.255.255: 56 data bytes 64 bytes from sanuki05.jp.iforce.net (10.16.67.5): icmp_seq=0. time=0.0850 ms 64 bytes from 10.16.11.15: icmp_seq=0. time=0.325 ms 64 bytes from 10.16.65.51: icmp_seq=0. time=0.485 ms 64 bytes from 10.16.11.13: icmp_seq=0. time=0.581 ms 64 bytes from 10.16.62.65: icmp_seq=0. time=0.666 ms ...
ルーティングテーブルを表示する
netstat -nr
"netstat -r" を実行するとルーティングテーブルを表示する事が出来ます。"default" の行がデフォルトゲートウェイです。経路情報が正しくないと宛先のホストに到達する事が出来ません。
# netstat -nr Routing Table: IPv4 Destination Gateway Flags Ref Use Interface -------------------- -------------------- ----- ----- ---------- --------- default 10.16.0.254 UG 1 1054 10.16.0.0 10.16.67.5 U 1 202 e1000g0 224.0.0.0 10.16.67.5 U 1 0 e1000g0 127.0.0.1 127.0.0.1 UH 4 77 lo0
"route -f" を実行するとルーティングテーブルを最新の状態に更新する事が出来ます。
# route -f
宛先ホストまでの経路を調べる
traceroute <HOSTNAME>
"traceroute <HOSTNAME>" を実行すると、宛先ホストまでの経路を調べる事が出来ます。
# traceroute 10.16.0.254 traceroute to 10.16.0.254 (10.16.0.254), 30 hops max, 40 byte packets 1 10.16.100.254 (10.16.100.254) 0.474 ms \* 0.493 ms
プロセスが開いているソケットを調べる
pfiles <PID>
"pfiles <PID>" を実行すると、プロセスが開いているファイルとソケットを調べる事が出来ます。以下の例では sshd プロセスがポート 22 番を開いている事が分かります。どのホスト宛にどんなオプションで幾つのセッションを張っているのかを把握する事はネットワークの解析に役立ちます。
# pfiles 570 570: /usr/lib/ssh/sshd Current rlimit: 256 file descriptors 0: S_IFCHR mode:0666 dev:284,0 ino:6815752 uid:0 gid:3 rdev:13,2 O_RDWR|O_LARGEFILE /devices/pseudo/mm@0:null 1: S_IFCHR mode:0666 dev:284,0 ino:6815752 uid:0 gid:3 rdev:13,2 O_RDWR|O_LARGEFILE /devices/pseudo/mm@0:null 2: S_IFCHR mode:0666 dev:284,0 ino:6815752 uid:0 gid:3 rdev:13,2 O_RDWR|O_LARGEFILE /devices/pseudo/mm@0:null 3: S_IFSOCK mode:0666 dev:291,0 ino:50619 uid:0 gid:0 size:0 O_RDWR|O_NONBLOCK SOCK_STREAM SO_REUSEADDR,SO_SNDBUF(49152),SO_RCVBUF(49152),IP_NEXTHOP(0.192.0.0) sockname: AF_INET6 :: port: 22 7: S_IFREG mode:0666 dev:285,1 ino:65539 uid:0 gid:0 size:0 O_RDWR|O_LARGEFILE /system/contract/process/template
以下のコマンドを実行すると全てのプロセスが開いているソケットをプロセス毎に調べる事が出来ます。
# ps -eo pid,fname | grep -v PID | while read pid fname; do echo ${fname}; pfiles ${pid} | egrep 'peername|sockname' ; done
プロセスが発行したシステムコールを監視する
truss -cp <PID>
"truss -cp <PID>" を実行するとプロセスが発行したシステムコールとその回数を調べる事が出来ます。ネットワークの送受信もシステムコールを利用しています。例えば以下の例は sshd をトレースした物ですが、read, write, shutdown 等はネットワークのトラフィックに使用されたシステムコールです。システムコールの種類や回数は性能に大きく影響します。予想したよりもスループットが出ていない場合は、このコマンドを試してみて下さい。なお、<PID> に指定するプロセス ID は pgrep コマンドで渡す事も可能です。
# truss -cp `pgrep -xn sshd` signals ------------ SIGCLD 1 total: 1 syscall seconds calls errors _exit .000 1 read .185 9804 1 write .152 9641 close .000 4 chmod .000 1 chown .000 1 brk .000 2 getuid .000 1 sigaction .000 1 setcontext .000 1 waitid .000 2 1 lwp_sigmask .128 19433 pollsys .081 9717 stat64 .000 1 shutdown .000 1 -------- ------ ---- sys totals: .549 48611 2 usr time: .256 elapsed: 5.690
truss -vall -E -p <PID>
"truss -vall -E -p <PID>" を実行するとプロセスが発行しているシステムコールを逐次表示する事が出来ます。システムコールに消費した時間、引数、返り値等も合わせて調べる事が出来るので、ネットワークの解析にも非常に有効です。snoop コマンドではパケットレベルでの通信内容を見る事が出来ましたが、truss を使用するとアプリケーションレベルで通信内容を調べる事が出来ます。以下の例では telnet で "ls" と入力しています。
# truss -vall -E -p `pgrep -xn telnet` pollsys(0x08047670, 2, 0x00000000, 0x00000000) (sleeping...) fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLRDNORM|POLLRDBAND rev=POLLOUT 0.0000 pollsys(0x08047670, 2, 0x00000000, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=POLLRDNORM fd=4 ev=POLLRDNORM|POLLRDBAND rev=0 0.0000 read(0, " l", 1024) = 1 0.0000 pollsys(0x08047670, 2, 0x08047748, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLOUT|POLLRDNORM|POLLRDBAND rev=POLLOUT timeout: 0.000000000 sec 0.0000 send(4, " l", 1, 0) = 1 0.0000 pollsys(0x08047670, 2, 0x08047748, 0x00000000) = 0 fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLRDNORM|POLLRDBAND rev=0 timeout: 0.000000000 sec 0.0000 pollsys(0x08047670, 2, 0x00000000, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLRDNORM|POLLRDBAND rev=POLLRDNORM 0.0000 recv(4, " l", 1024, 0) = 1 0.0000 pollsys(0x08047670, 3, 0x08047748, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=0 fd=1 ev=POLLOUT rev=POLLOUT fd=4 ev=POLLRDNORM|POLLRDBAND rev=0 timeout: 0.000000000 sec 0.0000 write(1, " l", 1) = 1 0.0000 pollsys(0x08047670, 2, 0x00000000, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=POLLRDNORM fd=4 ev=POLLRDNORM|POLLRDBAND rev=0 0.0000 read(0, " s", 1024) = 1 0.0000 pollsys(0x08047670, 2, 0x08047748, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLOUT|POLLRDNORM|POLLRDBAND rev=POLLOUT timeout: 0.000000000 sec 0.0000 send(4, " s", 1, 0) = 1 0.0000 pollsys(0x08047670, 2, 0x08047748, 0x00000000) = 0 fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLRDNORM|POLLRDBAND rev=0 timeout: 0.000000000 sec 0.0000 pollsys(0x08047670, 2, 0x00000000, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLRDNORM|POLLRDBAND rev=POLLRDNORM 0.0000 recv(4, " s", 1024, 0) = 1 0.0000 pollsys(0x08047670, 3, 0x08047748, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=0 fd=1 ev=POLLOUT rev=POLLOUT fd=4 ev=POLLRDNORM|POLLRDBAND rev=0 timeout: 0.000000000 sec 0.0000 write(1, " s", 1) = 1 0.0000 pollsys(0x08047670, 2, 0x00000000, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=POLLRDNORM fd=4 ev=POLLRDNORM|POLLRDBAND rev=0 0.0000 read(0, "\\r", 1024) = 1 0.0000 pollsys(0x08047670, 2, 0x08047748, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLOUT|POLLRDNORM|POLLRDBAND rev=POLLOUT timeout: 0.000000000 sec
truss -o <FILE> -vall -E -p <PID>
truss も snoop と同様にファイルにダンプする事が可能です。
# truss -o /var/tmp/truss01.dump -vall -E -p `pgrep -xn telnet`
ダンプしたファイルはテキストファイルです。
# head /var/tmp/truss01.dump pollsys(0x08047670, 2, 0x00000000, 0x00000000) (sleeping...) fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLRDNORM|POLLRDBAND rev=POLLOUT 0.0000 pollsys(0x08047670, 2, 0x00000000, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=POLLRDNORM fd=4 ev=POLLRDNORM|POLLRDBAND rev=0 0.0000 read(0, " l", 1024) = 1 0.0000 pollsys(0x08047670, 2, 0x08047748, 0x00000000) = 1 fd=0 ev=POLLRDNORM rev=0 fd=4 ev=POLLOUT|POLLRDNORM|POLLRDBAND rev=POLLOUT
プロセス毎にトラフィックを監視する
tcpwdist.d
DTrace Toolkit には tcpwdist.d というプロセス毎に TCP の送受信サイズの分布を調査するスクリプトが入っています。
# ./tcpwdist.d Tracing... Hit Ctrl-C to end. \^C PID: 0 CMD: sched\\0 value ------------- Distribution ------------- count 1 | 0 2 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 4 | 0 PID: 19299 CMD: /usr/sbin/dtrace -s ./tcpwdist.d\\0 value ------------- Distribution ------------- count 8 | 0 16 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1 32 | 0 PID: 19246 CMD: telnet 10.16.100.16\\0 value ------------- Distribution ------------- count 0 | 0 1 |@ 13 2 | 3 4 | 3 8 | 1 16 | 6 32 |@ 12 64 |@ 22 128 |@ 15 256 |@@@@@@@@@@@@@@@@ 258 512 |@@ 28 1024 |@@@@@@@@@@@@@@@@@@ 286 2048 | 0
おわりに
以上 Solaris のネットワーク解析ツールの内、基本的な物をご紹介しました。状況に応じて各コマンドを使い分け、問題解決にお役立て下さい。DTrace や kstat を使用すると、更に詳細な分析を行う事も可能です。是非ご活用下さい。