labunix's blog

labunixのラボUnix

ラウンドトリップ時間をpingのワンライナーで計測してみる。

■ラウンドトリップ時間をpingのワンライナーで計測してみる。
 公式は以下を使用する。パケットの往復時間。

 スループット (Kbps) = ( TCP ウィンドウサイズ (Bytes) * 8(Bit)) / RTT (sec)

■以下を参考に公式に問題が無いか確認する。
 下記の本文の通り、64Kbytesは64000bytesとして計算。

 第2回 ネットワーク遅延と高速化
 http://gihyo.jp/admin/serial/01/net_prac_tech/0002

$ for RTT in 1 5 10 15 20 50 100 200;do \
    echo "$RTT" | awk '{printf "RTTが%3dmsのとき、帯域 ≒ "((64000)*8)/($1*1000)"Mbps\n",$1}'; \
  done
RTTが  1msのとき、帯域 ≒ 512Mbps
RTTが  5msのとき、帯域 ≒ 102.4Mbps
RTTが 10msのとき、帯域 ≒ 51.2Mbps
RTTが 15msのとき、帯域 ≒ 34.1333Mbps
RTTが 20msのとき、帯域 ≒ 25.6Mbps
RTTが 50msのとき、帯域 ≒ 10.24Mbps
RTTが100msのとき、帯域 ≒ 5.12Mbps
RTTが200msのとき、帯域 ≒ 2.56Mbps

■pingはIPヘッダとICMPヘッダで構成され、
 ICMPヘッダは8で、オプション無しのIPヘッダは20バイトという説明がmanマニュアルにある。

$ man ping | grep -A 1 "IP.*ICMP ヘッダ"
     (``pings'') は IP と ICMP ヘッダを持ち、それに “struct timeval” が続
     き、そして、パケットの残りを埋めるために任意個の ``pad'' バイトがある。

$ man ping | grep -A 3 " *\-s packetsize"
     -s packetsize
             何バイトのデータが送られるかを指定する。デフォルトは 56 で、 ICMP
             ヘッダの 8 バイトを加えて、 64 バイトの ICMP データになる。 スー
             パーユーザーだけがこのオプションを使用できる。

$ man ping | grep オプションなし
     オプションなしの IP ヘッダは 20 バイトである。 ICMP ECHO_REQUEST パケット

■ウインドウサイズは16bitなので65535が最大。
 ※16ビットの値は「0を含めて」65536個

$ /sbin/ifconfig lo | awk '/MTU/ {print $4}' 
MTU:65536

■TCPヘッダは20byte、IPヘッダも20byte、MTUサイズが1500byteだとすると、
 フラグメントしないTCPウインドウサイズの理論的な最大値は以下となる。

 64 (Kbytes) * 1024 (bytes) / MTU 1500 (bytes) - IPヘッダサイズ 20 (bytes) - TCPヘッダサイズ 20 (bytes)

$ echo "$((64 * 1024 / (1500 - 20 - 20 )))" | awk '{print (1500-20-20)*$1}'
64240

$ for RTT in 1 5 10 15 20 50 100 200;do \
    echo "$RTT" | awk '{printf "RTTが%3dmsのとき、帯域 ≒ "((64240)*8)/($1*1000)"Mbps\n",$1}'; \
  done
RTTが  1msのとき、帯域 ≒ 513.92Mbps
RTTが  5msのとき、帯域 ≒ 102.784Mbps
RTTが 10msのとき、帯域 ≒ 51.392Mbps
RTTが 15msのとき、帯域 ≒ 34.2613Mbps
RTTが 20msのとき、帯域 ≒ 25.696Mbps
RTTが 50msのとき、帯域 ≒ 10.2784Mbps
RTTが100msのとき、帯域 ≒ 5.1392Mbps
RTTが200msのとき、帯域 ≒ 2.5696Mbps

■これから確認するツールはping。
 ICMPなので、TCPヘッダではなくICMPヘッダの8バイトの方を採用する。
 なお、0.1ms,0.2msの値も追加。

$ for RTT in 0.1 0.2 1 5 10 15 20 50 100 200;do \
    echo "$RTT" | awk '{printf "RTTが%4.1fmsのとき、帯域 ≒ "((65535-20-8)*8)/($1*1000)"Mbps\n",$1}'; \
  done
RTTが 0.1msのとき、帯域 ≒ 5240.56Mbps
RTTが 0.2msのとき、帯域 ≒ 2620.28Mbps
RTTが 1.0msのとき、帯域 ≒ 524.056Mbps
RTTが 5.0msのとき、帯域 ≒ 104.811Mbps
RTTが10.0msのとき、帯域 ≒ 52.4056Mbps
RTTが15.0msのとき、帯域 ≒ 34.9371Mbps
RTTが20.0msのとき、帯域 ≒ 26.2028Mbps
RTTが50.0msのとき、帯域 ≒ 10.4811Mbps
RTTが100.0msのとき、帯域 ≒ 5.24056Mbps
RTTが200.0msのとき、帯域 ≒ 2.62028Mbps

■実際のコマンド実行結果を確認してみる。

$ TCPWINDOW_SIZE=65535; \
  COUNT=10; \
  TARGETIP=127.0.0.1; \
  ping -c $COUNT -s $(($TCPWINDOW_SIZE-20-8)) $TARGETIP | \
    awk -F\/ '{print}/^rtt/ {print "帯域 ≒ "(('${TCPWINDOW_SIZE}'-20-8)*8)/($5*1000)"Mbps"}'
PING 127.0.0.1 (127.0.0.1) 65507(65535) bytes of data.
65515 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.137 ms
65515 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.113 ms
65515 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.207 ms
65515 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.162 ms
65515 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.161 ms
65515 bytes from 127.0.0.1: icmp_seq=6 ttl=64 time=0.180 ms
65515 bytes from 127.0.0.1: icmp_seq=7 ttl=64 time=0.167 ms
65515 bytes from 127.0.0.1: icmp_seq=8 ttl=64 time=0.156 ms
65515 bytes from 127.0.0.1: icmp_seq=9 ttl=64 time=0.121 ms
65515 bytes from 127.0.0.1: icmp_seq=10 ttl=64 time=0.164 ms

--- 127.0.0.1 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 8998ms
rtt min/avg/max/mdev = 0.113/0.156/0.207/0.030 ms
帯域 ≒ 3359.33Mbps

$ echo "0.156" | awk '{printf "RTTが%4.3fms のとき、帯域 ≒ "((65535-20-8)*8)/($1*1000)"Mbps\n",$1}'
RTTが0.156msのとき、帯域 ≒ 3359.33Mbps

■MTU、パケットサイズを超えるフラグメントを許可しない環境でも行えるテストは以下。

$ /sbin/ifconfig eth0 | awk '/MTU/ {print $5}'
MTU:1500

$ TCPWINDOW_SIZE=1499; \
  COUNT=10; \
  TARGETIP=172.31.31.252; \
  ping -c $COUNT -s $(($TCPWINDOW_SIZE-20-8)) $TARGETIP | \
    awk -F\/ '{print}/^rtt/ {print "帯域 ≒ "(('${TCPWINDOW_SIZE}'-20-8)*8)/($5*1000)"Mbps"}'
PING 172.31.31.252 (172.31.31.252) 1471(1499) bytes of data.
1479 bytes from 172.31.31.252: icmp_seq=1 ttl=254 time=1.54 ms
1479 bytes from 172.31.31.252: icmp_seq=2 ttl=254 time=1.34 ms
1479 bytes from 172.31.31.252: icmp_seq=3 ttl=254 time=1.31 ms
1479 bytes from 172.31.31.252: icmp_seq=4 ttl=254 time=1.28 ms
1479 bytes from 172.31.31.252: icmp_seq=5 ttl=254 time=1.24 ms
1479 bytes from 172.31.31.252: icmp_seq=6 ttl=254 time=1.26 ms
1479 bytes from 172.31.31.252: icmp_seq=7 ttl=254 time=1.32 ms
1479 bytes from 172.31.31.252: icmp_seq=8 ttl=254 time=1.27 ms
1479 bytes from 172.31.31.252: icmp_seq=9 ttl=254 time=1.27 ms
1479 bytes from 172.31.31.252: icmp_seq=10 ttl=254 time=1.34 ms

--- 172.31.31.252 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9013ms
rtt min/avg/max/mdev = 1.246/1.321/1.546/0.084 ms
帯域 ≒ 8.9084Mbps

$ echo "1.321" | awk '{printf "RTTが%4.3fms のとき、帯域 ≒ "((1499-20-8)*8)/($1*1000)"Mbps\n",$1}'
RTTが1.321ms のとき、帯域 ≒ 8.9084Mbps

■フラグメントしても問題ない環境でのテストは以下。

$ TCPWINDOW_SIZE=65535; \
  COUNT=10; \
  TARGETIP=172.31.31.252; \
  ping -c $COUNT -s $(($TCPWINDOW_SIZE-20-8)) $TARGETIP | \
    awk -F\/ '{print}/^rtt/ {print "帯域 ≒ "(('${TCPWINDOW_SIZE}'-20-8)*8)/($5*1000)"Mbps"}'
PING 172.31.31.252 (172.31.31.252) 65507(65535) bytes of data.
65515 bytes from 172.31.31.252: icmp_seq=1 ttl=254 time=19.9 ms
65515 bytes from 172.31.31.252: icmp_seq=2 ttl=254 time=19.8 ms
65515 bytes from 172.31.31.252: icmp_seq=3 ttl=254 time=19.8 ms
65515 bytes from 172.31.31.252: icmp_seq=4 ttl=254 time=20.1 ms
65515 bytes from 172.31.31.252: icmp_seq=5 ttl=254 time=19.8 ms
65515 bytes from 172.31.31.252: icmp_seq=6 ttl=254 time=19.7 ms
65515 bytes from 172.31.31.252: icmp_seq=7 ttl=254 time=19.8 ms
65515 bytes from 172.31.31.252: icmp_seq=8 ttl=254 time=19.8 ms
65515 bytes from 172.31.31.252: icmp_seq=9 ttl=254 time=19.7 ms
65515 bytes from 172.31.31.252: icmp_seq=10 ttl=254 time=19.8 ms

--- 172.31.31.252 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9010ms
rtt min/avg/max/mdev = 19.781/19.870/20.120/0.192 ms
帯域 ≒ 26.3742Mbps

$ echo "19.870" | awk '{printf "RTTが%4.3fms のとき、帯域 ≒ "((65535-20-8)*8)/($1*1000)"Mbps\n",$1}'
RTTが19.870ms のとき、帯域 ≒ 26.3742Mbps

■pingの経路を記録するには、更に40byte必要となる。
 ただし、オプションをセットしても無視される可能性もある。

$ man ping | grep -A2 "\-R"
     -R      経路を記録。 ECHO_REQUEST パケットに RECORD_ROUTE オプションを設定し、返ってきたパケットの経路バッファ (route buffer)
             を表示する。 IP ヘッダは 9 つの経路分の大きさしかないことに注意せよ。 また、多くのホストはこのオプションを無視する
             か、破棄してしまう。

$ TCPWINDOW_SIZE=65535; \
  COUNT=10; \
  TARGETIP=172.31.31.252; \
  ping -R -c $COUNT -s $(($TCPWINDOW_SIZE-20-8-40)) $TARGETIP | \
    awk -F\/ '{print}/^rtt/ {print "帯域 ≒ "(('${TCPWINDOW_SIZE}'-20-8)*8)/($5*1000)"Mbps"}'
PING 172.31.31.252 (172.31.31.252) 65467(65535) bytes of data.
65475 bytes from 172.31.31.252: icmp_seq=1 ttl=254 time=20.3 ms
65475 bytes from 172.31.31.252: icmp_seq=2 ttl=254 time=20.0 ms
65475 bytes from 172.31.31.252: icmp_seq=3 ttl=254 time=20.1 ms
65475 bytes from 172.31.31.252: icmp_seq=4 ttl=254 time=20.2 ms
65475 bytes from 172.31.31.252: icmp_seq=5 ttl=254 time=20.0 ms
65475 bytes from 172.31.31.252: icmp_seq=6 ttl=254 time=20.0 ms
65475 bytes from 172.31.31.252: icmp_seq=7 ttl=254 time=20.0 ms
65475 bytes from 172.31.31.252: icmp_seq=8 ttl=254 time=20.1 ms
65475 bytes from 172.31.31.252: icmp_seq=9 ttl=254 time=20.1 ms
65475 bytes from 172.31.31.252: icmp_seq=10 ttl=254 time=20.1 ms

--- 172.31.31.252 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9012ms
rtt min/avg/max/mdev = 20.042/20.127/20.323/0.187 ms
帯域 ≒ 26.0375Mbps