hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

iftopコマンドの使い方

1 iftopコマンドとは?

帯域を測定するツールです。TCPコネクション単位で帯域を確認することができます。iftopの動作確認をするためiperf3コマンドを使います。iperf3の使い方は、iperf3コマンドの使い方 - hana_shinのLinux技術ブログを参照ください。

2 検証環境

2.1 ネットワーク構成

VMware Workstation 15 Player上の仮想マシンを使いました。サーバとクライアントの2台構成です。図中のens33はNICの名前です。

                               192.168.2.0/24
client(ens33) ------------------------------------------(ens33) server
        .105                                           .120

2.2 版数

CentOS版数は、サーバ、クライアントともに下記版数です。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname  -r
3.10.0-1160.el7.x86_64

3 インストール方法

iftopパッケージはepelリポジトリにあるので、まず、epel-releaseパッケージをインストールします。

[root@client ~]# yum -y install epel-release

クライアントにiftopパッケージをインストールします。

[root@client ~]# yum -y install iftop

iftopコマンドの版数を確認します。

[root@client ~]# iftop -h
-snip-
iftop, version 1.0pre4

4 事前準備

サーバでポート番号を開放します。iperf3は,TCP/UDPの5201番ポートを使うので、5201番ポートへのアクセスを許可しておきます。まず、TCPの5201番ポートを開放します。

[root@server ~]# firewall-cmd --add-port=5201/tcp
success

ポートの状態を確認します。TCPの5201番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
5201/tcp

次に、UDPの5201番ポートを開放します。

[root@server ~]# firewall-cmd --add-port=5201/udp
success

ポートの状態を確認します。UDPの5201番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
5201/tcp 5201/udp

5 オプション一覧

オプションは以下のとおりです。

[root@client ~]#  iftop -h
iftop: display bandwidth usage on an interface by host

Synopsis: iftop -h | [-npblNBP] [-i interface] [-f filter code]
                               [-F net/mask] [-G net6/mask6]

   -h                  display this message
   -n                  don't do hostname lookups
   -N                  don't convert port numbers to services
   -p                  run in promiscuous mode (show traffic between other
                       hosts on the same network segment)
   -b                  don't display a bar graph of traffic
   -B                  display bandwidth in bytes
   -a                  display bandwidth in packets
   -i interface        listen on named interface
   -f filter code      use filter code to select packets to count
                       (default: none, but only IP packets are counted)
   -F net/mask         show traffic flows in/out of IPv4 network
   -G net6/mask6       show traffic flows in/out of IPv6 network
   -l                  display and count link-local IPv6 traffic (default: off)
   -P                  show ports as well as hosts
   -m limit            sets the upper limit for the bandwidth scale
   -c config file      specifies an alternative configuration file
   -t                  use text interface without ncurses

   Sorting orders:
   -o 2s                Sort by first column (2s traffic average)
   -o 10s               Sort by second column (10s traffic average) [default]
   -o 40s               Sort by third column (40s traffic average)
   -o source            Sort by source address
   -o destination       Sort by destination address

   The following options are only available in combination with -t
   -s num              print one single text output afer num seconds, then quit
   -L num              number of lines to print

iftop, version 1.0pre4
copyright (c) 2002 Paul Warren <pdw@ex-parrot.com> and contributors

6 基本的な使い方

iperf3コマンドを使って、クライアントからサーバに10(Mbit/s)、30(Mbit/s)でパケットを送信してみます。このとき、iftopコマンドでクライアント/サーバ間の帯域を確認してみます。

6.1 10(Mbit/s)の場合

サーバでiperf3コマンドを実行します。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server	listening	on	5201
-----------------------------------------------------------

クライアントでiperf3を実行します。テスト時間は600秒、帯域は10(Mbit/s)を指定します。

[root@client ~]# iperf3 -c server -t 600 -b 10M

クライアントでターミナルをもう1つ開き、iftopコマンドを実行します。
直近2秒の帯域は、クライアントからサーバは9.52Mb(★)、サーバからクライアンは7.72Kb(●)であることがわかります。

[root@client ~]# iftop -i ens33 -t
-snip-
============================================================================================

   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 client                                   =>   ★9.52Mb     9.52Mb     9.27Mb     37.1MB
     server                                   <=   ●7.72Kb     10.3Kb     8.53Kb     34.1KB
--------------------------------------------------------------------------------------------
Total send rate:                                     9.52Mb     9.53Mb     9.27Mb
Total receive rate:                                  9.38Kb     11.0Kb     9.21Kb
Total send and receive rate:                         9.53Mb     9.54Mb     9.28Mb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                     10.0Mb     12.1Kb     10.0Mb
Cumulative (sent/received/total):                    37.1MB     36.8KB     37.1MB
============================================================================================
-snip-

6.2 30(Mbit/s)の場合

サーバでiperf3コマンドを実行します。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server	listening	on	5201
-----------------------------------------------------------

クライアントでiperf3を実行します。テスト時間は600秒、帯域は30(Mbit/s)を指定します。

[root@client ~]# iperf3 -c server -t 600 -b 30M

クライアントでターミナルをもう1つ開き、iftopコマンドを実行します。
直近2秒の帯域は、クライアントからサーバは28.5Mb(★)、サーバからクライアンは15.8Kb(●)であることがわかります。

[root@client ~]# iftop -i ens33 -t
-snip-
============================================================================================

   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 client                                   =>   ★28.5Mb     28.9Mb     28.9Mb      159MB
     server                                   <=   ●15.8Kb     16.9Kb     17.3Kb     98.7KB
--------------------------------------------------------------------------------------------
Total send rate:                                     28.5Mb     28.9Mb     28.9Mb
Total receive rate:                                  16.4Kb     17.6Kb     18.1Kb
Total send and receive rate:                         28.6Mb     29.0Mb     28.9Mb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                     30.0Mb     22.7Kb     30.1Mb
Cumulative (sent/received/total):                     159MB      106KB      159MB
============================================================================================
-snip-

7 ホスト名をIPアドレスで表示する方法(-n)

"-n"はホスト名をIPアドレスで表示するオプションです。

サーバでiperf3コマンドを実行します。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server	listening	on	5201
-----------------------------------------------------------

クライアントでiperf3を実行します。テスト時間は600秒、帯域は10(Mbit/s)を指定します。

[root@client ~]# iperf3 -c server -t 600 -b 10M

クライアントでターミナルをもう1つ開き、iftopコマンドを実行します。ホスト名(server,client)ではなくIPアドレスが表示されていることがわかります。

[root@client ~]# iftop -i ens33 -t -n
-snip-
============================================================================================

   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 192.168.2.105                            =>     9.52Mb     9.52Mb     8.23Mb     41.2MB
     192.168.2.100                            <=     7.72Kb     8.08Kb     7.86Kb     39.3KB
--------------------------------------------------------------------------------------------
Total send rate:                                     9.52Mb     9.52Mb     8.24Mb
Total receive rate:                                  8.26Kb     8.62Kb     8.71Kb
Total send and receive rate:                         9.53Mb     9.53Mb     8.25Mb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                     10.0Mb     13.7Kb     10.0Mb
Cumulative (sent/received/total):                    2.98GB     1.24MB     2.98GB
============================================================================================
-snip-

8 ポート番号を表示する方法(-P)

"-P"はポート番号を表示するオプションです。

サーバでiperf3コマンドを実行します。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server	listening	on	5201
-----------------------------------------------------------

クライアントでiperf3を実行します。テスト時間は600秒、帯域は10(Mbit/s)を指定します。

[root@client ~]# iperf3 -c server -t 600 -b 10M

クライアントでターミナルをもう1つ開き、iftopコマンドを実行します。ポート番号(54122)、サービス名(targus-getdata1)が表示されていることがわかります。サービス名は/etc/servicesで定義されていて、5201番であることがわかります。

[root@client ~]# iftop -i ens33 -t -P
-snip-
============================================================================================

   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 client:54122                             =>     9.52Mb     8.02Mb     8.02Mb     6.01MB
     server:targus-getdata1                   <=     7.72Kb     8.19Kb     8.19Kb     6.14KB
--------------------------------------------------------------------------------------------
Total send rate:                                     9.52Mb     8.02Mb     8.02Mb
Total receive rate:                                  8.26Kb     8.93Kb     8.93Kb
Total send and receive rate:                         9.53Mb     8.03Mb     8.03Mb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                     10.0Mb     12.5Kb     10.0Mb
Cumulative (sent/received/total):                    6.02MB     6.69KB     6.02MB
============================================================================================

9 実行時間を指定する方法(-s)

"-s"はiftopコマンドを実行する時間(秒)を指定するオプションです。指定した時間が経過すると、iftopコマンドが終了します。

今までと同じようにiperf3を実行します。今回はiperf3実行の様子は記載省略します。このとき、”-s”に10秒を指定してiftopコマンドを実行してみます。20:49:57に開始して20:50:07に終了していることがわかります。

[root@client ~]# date;iftop -i ens33 -t -s 10 ; date
2022年  1月 16日 日曜日 20:49:57 JST
interface: ens33
IP address is: 192.168.2.37
MAC address is: 00:0c:29:bf:f4:53
Listening on ens33
   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 client                                   =>     9.52Mb     8.61Mb     8.61Mb     10.8MB
     server                                   <=     7.72Kb     7.64Kb     7.64Kb     9.55KB
   2 224.0.0.251                              =>         0b         0b         0b         0B
     hananoiPad                               <=     2.78Kb     3.35Kb     3.35Kb     4.19KB
   3 client                                   =>     1.22Kb     1.18Kb     1.18Kb     1.48KB
     DESKTOP-MB6G4KI                          <=       368b       368b       368b       460B
   4 client                                   =>         0b       226b       226b       282B
     WRC-2533GS2                              <=         0b       334b       334b       417B
--------------------------------------------------------------------------------------------
Total send rate:                                     9.52Mb     8.62Mb     8.62Mb
Total receive rate:                                  10.9Kb     11.7Kb     11.7Kb
Total send and receive rate:                         9.53Mb     8.63Mb     8.63Mb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                     9.52Mb     17.0Kb     9.54Mb
Cumulative (sent/received/total):                    10.8MB     14.6KB     10.8MB
============================================================================================

2022年  1月 16日 日曜日 20:50:07 JST

10 filter codeの使い方(-f)

filter codeを使うと、特定の通信の帯域だけを表示することができます。たとえば、ポート番号やIPアドレスを指定することで、特定の通信だけに絞り込むことが可能です。

10.1 ポート番号で絞り込む方法(dst port/src port)

サーバでiperf3コマンドを実行します。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server	listening	on	5201
-----------------------------------------------------------

クライアントでiperf3を実行します。テスト時間は600秒、帯域は10(Mbit/s)を指定します。

[root@client ~]# iperf3 -c server -t 600 -b 10M

クライアントでターミナルをもう1つ開きます。"dst port 5201"と指定してクライアントからサーバに向けての通信に絞り込んでみます。クライアントからサーバへの通信の帯域は約9.0Mbであることがわかります。一方、サーバからクライアントへの通信の帯域は0であることがわかります。

[root@client ~]# iftop -i ens33 -t -nP -f "dst port 5201"
-snip-
============================================================================================

   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 192.168.2.105:48552                      =>     9.52Mb     9.62Mb     8.95Mb     17.9MB
     192.168.2.100:targus-getdata1            <=         0b         0b         0b         0B
--------------------------------------------------------------------------------------------
Total send rate:                                     9.52Mb     9.62Mb     8.95Mb
Total receive rate:                                      0b         0b         0b
Total send and receive rate:                         9.52Mb     9.62Mb     8.95Mb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                     10.0Mb         0b     10.0Mb
Cumulative (sent/received/total):                    17.9MB         0B     17.9MB
============================================================================================
-snip-

今度は、サーバからクライアントに向けての通信に絞り込んでみます。"src port 5201"と指定してサーバからクライアントに向けての通信に絞り込んでみます。クライアントからサーバへの通信の帯域は0であることがわかります。一方、サーバからクライアントへの通信の帯域は約7Kbであることがわかります。この帯域は、サーバからクライアントに送信されるTCPのACKによるものです。

[root@client ~]# iftop -i ens33 -t -nP -f "src port 5201"
-snip-
============================================================================================

   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 192.168.2.105:48552                      =>         0b         0b         0b         0B
     192.168.2.100:targus-getdata1            <=     7.72Kb     7.80Kb     7.11Kb     10.7KB
--------------------------------------------------------------------------------------------
Total send rate:                                         0b         0b         0b
Total receive rate:                                  7.72Kb     7.80Kb     7.11Kb
Total send and receive rate:                         7.72Kb     7.80Kb     7.11Kb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                         0b     8.12Kb     8.12Kb
Cumulative (sent/received/total):                        0B     10.7KB     10.7KB
============================================================================================
-snip-

10.2 IPアドレスで絞り込む方法(dst host/src host)

考え方はポート番号の時と同じですので、詳細の説明は割愛します。
送信先Pアドレスが"192.168.2.10"の通信を絞り込んでみます。

[root@client ~]# iftop -i ens33 -t -nP -f "dst host 192.168.2.100"
-snip-
============================================================================================

   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 192.168.2.105:48552                      =>     9.52Mb     8.56Mb     8.56Mb     10.7MB
     192.168.2.100:targus-getdata1            <=         0b         0b         0b         0B
--------------------------------------------------------------------------------------------
Total send rate:                                     9.52Mb     8.56Mb     8.56Mb
Total receive rate:                                      0b         0b         0b
Total send and receive rate:                         9.52Mb     8.56Mb     8.56Mb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                     10.0Mb         0b     10.0Mb
Cumulative (sent/received/total):                    10.7MB         0B     10.7MB
============================================================================================

今度は、送信元IPアドレスが"192.168.2.10"の通信を絞り込んでみます。

[root@client ~]# iftop -i ens33 -t -nP -f "src host 192.168.2.100"
-snip-
============================================================================================

   # Host name (port/service if enabled)            last 2s   last 10s   last 40s cumulative
--------------------------------------------------------------------------------------------
   1 192.168.2.105:48552                      =>         0b         0b         0b         0B
     192.168.2.100:targus-getdata1            <=     7.72Kb     7.24Kb     7.24Kb     5.43KB
--------------------------------------------------------------------------------------------
Total send rate:                                         0b         0b         0b
Total receive rate:                                  7.72Kb     7.24Kb     7.24Kb
Total send and receive rate:                         7.72Kb     7.24Kb     7.24Kb
--------------------------------------------------------------------------------------------
Peak rate (sent/received/total):                         0b     8.53Kb     8.53Kb
Cumulative (sent/received/total):                        0B     5.43KB     5.43KB
============================================================================================

iperf3コマンドの使い方

1 iperf3コマンドとは?

利用可能なネットワークの最大帯域の測定や、指定した帯域の負荷をかけるためのものです。

2 検証環境

2.1 ネットワーク構成

VMware Workstation 15 Player上の仮想マシンを使いました。サーバとクライアントの2台構成です。図中のens33はNICの名前です。IPv4アドレスの他に、IPv6リンクローカルアドレスも記載しています。

                               192.168.2.0/24
client(ens33) ------------------------------------------(ens33) server
        .105                                           .120
fe80::3e2:a9c0:9fa6:a8ad/64                            fe80::174:936a:8876:8055/64

2.2 版数

CentOS版数は、サーバ、クライアントともに下記版数です。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname  -r
3.10.0-1160.el7.x86_64

3 インストール方法

サーバとクライアントでiperf3パッケージをインストールします。

[root@server ~]#  yum -y install iperf3

iperf3コマンドの版数を確認します。

[root@server ~]# iperf3 -v
iperf 3.1.7
Linux server 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
Optional features available: CPU affinity setting, IPv6 flow label, TCP congestion algorithm setting, sendfile / zerocopy, socket pacing

4 事前準備

サーバでポート番号を開放します。iperf3は,TCP/UDPの5201番ポートを使うので、5201番ポートへのアクセスを許可しておきます。まず、TCPの5201番ポートを開放します。

[root@server ~]# firewall-cmd --add-port=5201/tcp
success

ポートの状態を確認します。TCPの5201番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
5201/tcp

次に、UDPの5201番ポートを開放します。

[root@server ~]# firewall-cmd --add-port=5201/udp
success

ポートの状態を確認します。UDPの5201番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
5201/tcp 5201/udp

5 オプション一覧

オプション一覧を確認します。

[root@server ~]# iperf3 -h
Usage: iperf [-s|-c host] [options]
       iperf [-h|--help] [-v|--version]

Server or Client:
  -p, --port      #         server port to listen on/connect to
  -f, --format    [kmgKMG]  format to report: Kbits, Mbits, KBytes, MBytes
  -i, --interval  #         seconds between periodic bandwidth reports
  -F, --file name           xmit/recv the specified file
  -A, --affinity n/n,m      set CPU affinity
  -B, --bind      <host>    bind to a specific interface
  -V, --verbose             more detailed output
  -J, --json                output in JSON format
  --logfile f               send output to a log file
  --forceflush              force flushing output at every interval
  -d, --debug               emit debugging output
  -v, --version             show version information and quit
  -h, --help                show this message and quit
Server specific:
  -s, --server              run in server mode
  -D, --daemon              run the server as a daemon
  -I, --pidfile file        write PID file
  -1, --one-off             handle one client connection then exit
Client specific:
  -c, --client    <host>    run in client mode, connecting to <host>
  -u, --udp                 use UDP rather than TCP
  -b, --bandwidth #[KMG][/#] target bandwidth in bits/sec (0 for unlimited)
                            (default 1 Mbit/sec for UDP, unlimited for TCP)
                            (optional slash and packet count for burst mode)
  --fq-rate #[KMG]          enable fair-queuing based socket pacing in
                            bits/sec (Linux only)
  -t, --time      #         time in seconds to transmit for (default 10 secs)
  -n, --bytes     #[KMG]    number of bytes to transmit (instead of -t)
  -k, --blockcount #[KMG]   number of blocks (packets) to transmit (instead of -t or -n)
  -l, --len       #[KMG]    length of buffer to read or write
                            (default 128 KB for TCP, dynamic or 1 for UDP)
  --cport         <port>    bind to a specific client port (TCP and UDP, default: ephemeral port)
  -P, --parallel  #         number of parallel client streams to run
  -R, --reverse             run in reverse mode (server sends, client receives)
  -w, --window    #[KMG]    set window size / socket buffer size
  -C, --congestion <algo>   set TCP congestion control algorithm (Linux and FreeBSD only)
  -M, --set-mss   #         set TCP/SCTP maximum segment size (MTU - 40 bytes)
  -N, --no-delay            set TCP/SCTP no delay, disabling Nagle's Algorithm
  -4, --version4            only use IPv4
  -6, --version6            only use IPv6
  -S, --tos N               set the IP 'type of service'
  -L, --flowlabel N         set the IPv6 flow label (only supported on Linux)
  -Z, --zerocopy            use a 'zero copy' method of sending data
  -O, --omit N              omit the first n seconds
  -T, --title str           prefix every output line with this string
  --get-server-output       get results from server
  --udp-counters-64bit      use 64-bit counters in UDP test packets

[KMG] indicates options that support a K/M/G suffix for kilo-, mega-, or giga-

iperf3 homepage at: http://software.es.net/iperf/
Report bugs to:     https://github.com/esnet/iperf

6 利用可能な最大帯域を測定する方法(もっとも基本的な使い方)

クライアントからサーバにTCPパケットを送信して帯域を測定します。

サーバでiperf3コマンドを実行します。このとき、-sオプションを指定します。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

クライアントでiperf3コマンドを実行します。このとき、-cオプションを指定します。サーバのIPアドレスには192.168.0.10を指定します。測定した送信側、受信側の帯域が1.56Gbits/secであることがわかります。

[root@client ~]# iperf3 -c 192.168.2.100
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 51136 connected to 192.168.2.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec   149 MBytes  1.25 Gbits/sec    0   1.18 MBytes
[  4]   1.00-2.00   sec   175 MBytes  1.47 Gbits/sec    9   1.44 MBytes
[  4]   2.00-3.00   sec   181 MBytes  1.52 Gbits/sec    0   1.57 MBytes
[  4]   3.00-4.00   sec   188 MBytes  1.57 Gbits/sec    0   1.68 MBytes
[  4]   4.00-5.00   sec   181 MBytes  1.52 Gbits/sec    0   1.76 MBytes
[  4]   5.00-6.00   sec   171 MBytes  1.44 Gbits/sec    0   1.82 MBytes
[  4]   6.00-7.00   sec   182 MBytes  1.53 Gbits/sec    4   1.37 MBytes
[  4]   7.00-8.00   sec   205 MBytes  1.72 Gbits/sec    0   1.46 MBytes
[  4]   8.00-9.00   sec   208 MBytes  1.74 Gbits/sec    0   1.54 MBytes
[  4]   9.00-10.00  sec   218 MBytes  1.82 Gbits/sec    0   1.61 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  1.81 GBytes  1.56 Gbits/sec   13             sender
[  4]   0.00-10.00  sec  1.81 GBytes  1.56 Gbits/sec                  receiver

iperf Done.

サーバの実行結果は以下のとおりです。受信側の帯域が1.55Gbits/secであることがわかります。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 192.168.2.105, port 51134
[  5] local 192.168.2.100 port 5201 connected to 192.168.2.105 port 51136
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-1.00   sec   142 MBytes  1.19 Gbits/sec
[  5]   1.00-2.00   sec   173 MBytes  1.45 Gbits/sec
[  5]   2.00-3.00   sec   182 MBytes  1.53 Gbits/sec
[  5]   3.00-4.00   sec   187 MBytes  1.57 Gbits/sec
[  5]   4.00-5.00   sec   181 MBytes  1.52 Gbits/sec
[  5]   5.00-6.00   sec   170 MBytes  1.43 Gbits/sec
[  5]   6.00-7.00   sec   184 MBytes  1.54 Gbits/sec
[  5]   7.00-8.00   sec   204 MBytes  1.71 Gbits/sec
[  5]   8.00-9.00   sec   208 MBytes  1.75 Gbits/sec
[  5]   9.00-10.01  sec   217 MBytes  1.81 Gbits/sec
[  5]  10.01-10.04  sec  8.56 MBytes  2.00 Gbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-10.04  sec  0.00 Bytes  0.00 bits/sec                  sender
[  5]   0.00-10.04  sec  1.81 GBytes  1.55 Gbits/sec                  receiver
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

7 実行時間を指定する方法(-t)

帯域測定の時間はデフォルトで10秒です。-tオプションを使うことで、測定時間を変更することができます。ここでは、5秒間実行してみます。サーバでiperf3コマンドを実行します。このとき、-sオプションを指定します。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

クライアントでiperf3コマンドを実行します。このとき、-tオプションを指定して測定時間を指定します。ここでは5秒間帯域の測定をしてみます。

[root@client ~]# iperf3 -c 192.168.2.100 -t 5
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 51140 connected to 192.168.2.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec   163 MBytes  1.37 Gbits/sec    0   1.59 MBytes
[  4]   1.00-2.00   sec   168 MBytes  1.41 Gbits/sec   45   1.48 MBytes
[  4]   2.00-3.00   sec   161 MBytes  1.35 Gbits/sec    0   1.61 MBytes
[  4]   3.00-4.00   sec   215 MBytes  1.80 Gbits/sec    0   1.71 MBytes
[  4]   4.00-5.01   sec   215 MBytes  1.80 Gbits/sec    0   1.78 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-5.01   sec   922 MBytes  1.54 Gbits/sec   45             sender
[  4]   0.00-5.01   sec   919 MBytes  1.54 Gbits/sec                  receiver

iperf Done.

サーバでiperf3コマンドの実行結果を確認します。最終結果に"0.00-5.05"と表示されているため、帯域の測定時間が5秒であることがわかります。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 192.168.2.105, port 51138
[  5] local 192.168.2.100 port 5201 connected to 192.168.2.105 port 51140
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-1.00   sec   154 MBytes  1.29 Gbits/sec
[  5]   1.00-2.00   sec   168 MBytes  1.41 Gbits/sec
[  5]   2.00-3.00   sec   161 MBytes  1.35 Gbits/sec
[  5]   3.00-4.00   sec   215 MBytes  1.80 Gbits/sec
[  5]   4.00-5.00   sec   214 MBytes  1.80 Gbits/sec
[  5]   5.00-5.05   sec  8.27 MBytes  1.49 Gbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-5.05   sec  0.00 Bytes  0.00 bits/sec                  sender
[  5]   0.00-5.05   sec   919 MBytes  1.53 Gbits/sec                  receiver

8 帯域を指定する方法(-b)

このオプションは、利用可能な最大帯域を測定する、という目的に使うのではなく、指定した帯域の負荷をかけた状態で他の様々なテストを行うときに使うものです。
サーバの実行方法は、これまでの方法と同じなので、ここでは省略します。クライアントの実行結果のみを記載します。

8.1 100(Mbits/sec)を指定した場合

クライアントの実行結果を確認します。送信側、受信側の最大帯域が99.1Mbits/secであることがわかります。

[root@client ~]# iperf3 -c 192.168.2.100 -b 100M
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 51144 connected to 192.168.2.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec  11.0 MBytes  91.7 Mbits/sec    0    140 KBytes
[  4]   1.00-2.00   sec  11.9 MBytes  99.8 Mbits/sec    0    195 KBytes
[  4]   2.00-3.00   sec  11.9 MBytes  99.6 Mbits/sec    0    257 KBytes
[  4]   3.00-4.00   sec  11.9 MBytes  99.5 Mbits/sec    0    294 KBytes
[  4]   4.00-5.00   sec  12.0 MBytes   101 Mbits/sec    0    331 KBytes
[  4]   5.00-6.00   sec  11.9 MBytes  99.8 Mbits/sec    0    365 KBytes
[  4]   6.00-7.00   sec  11.9 MBytes  99.3 Mbits/sec    0    393 KBytes
[  4]   7.00-8.00   sec  12.0 MBytes   101 Mbits/sec    0    417 KBytes
[  4]   8.00-9.00   sec  11.9 MBytes  99.5 Mbits/sec    0    428 KBytes
[  4]   9.00-10.00  sec  12.0 MBytes   101 Mbits/sec    0    441 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec   118 MBytes  99.1 Mbits/sec    0             sender
[  4]   0.00-10.00  sec   118 MBytes  99.1 Mbits/sec                  receiver

iperf Done.

8.2 50(Mbits/sec)を指定した場合

クライアントの実行結果を確認します。送信側、受信側の最大帯域が49.6Mbits/sec であることがわかります。

[root@client ~]# iperf3 -c 192.168.2.100 -b 50M
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 51148 connected to 192.168.2.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec  5.51 MBytes  46.2 Mbits/sec    0   93.3 KBytes
[  4]   1.00-2.00   sec  5.88 MBytes  49.2 Mbits/sec    0    126 KBytes
[  4]   2.00-3.00   sec  6.00 MBytes  50.4 Mbits/sec    0    146 KBytes
[  4]   3.00-4.00   sec  6.00 MBytes  50.2 Mbits/sec    0    165 KBytes
[  4]   4.00-5.00   sec  5.88 MBytes  49.3 Mbits/sec    0    185 KBytes
[  4]   5.00-6.00   sec  6.00 MBytes  50.3 Mbits/sec    0    201 KBytes
[  4]   6.00-7.00   sec  6.00 MBytes  50.3 Mbits/sec    0    226 KBytes
[  4]   7.00-8.00   sec  5.88 MBytes  49.3 Mbits/sec    0    245 KBytes
[  4]   8.00-9.00   sec  6.00 MBytes  50.4 Mbits/sec    0    255 KBytes
[  4]   9.00-10.00  sec  6.00 MBytes  50.2 Mbits/sec    0    263 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  59.1 MBytes  49.6 Mbits/sec    0             sender
[  4]   0.00-10.00  sec  59.1 MBytes  49.6 Mbits/sec                  receiver

iperf Done.

9 測定に使用するインタフェースを指定する方法(-B)

複数のインタフェースがある場合に使用するオプションです。-Bオプションで指定したインタフェースから帯域測定用のパケットを送信するようになります。このオプションは、サーバとクライアントで使うことができます。私の検証環境ではサーバ、クライアントともにインタフェースが1つしかないので、あまり意味はありませんが、使い方を説明するため、それぞれのインタフェースを指定してみます。

サーバでiperf3コマンドを実行します。このとき、-Bオプションを指定してサーバのインタフェースを指定します。

[root@server ~]# iperf3 -s -B 192.168.2.100
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

クライアントでiperf3コマンドを実行します。このとき、-Bオプションを指定してクライアントのインタフェースを指定します。

[root@client ~]# iperf3 -c 192.168.2.100 -B 192.168.2.105
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 44045 connected to 192.168.2.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec   175 MBytes  1.46 Gbits/sec    0    814 KBytes
[  4]   1.00-2.00   sec   170 MBytes  1.43 Gbits/sec   26   1.38 MBytes
[  4]   2.00-3.00   sec   186 MBytes  1.56 Gbits/sec    0   1.52 MBytes
[  4]   3.00-4.00   sec   184 MBytes  1.54 Gbits/sec    0   1.64 MBytes
[  4]   4.00-5.00   sec   199 MBytes  1.67 Gbits/sec    0   1.73 MBytes
[  4]   5.00-6.00   sec   214 MBytes  1.79 Gbits/sec    0   1.80 MBytes
[  4]   6.00-7.00   sec   218 MBytes  1.83 Gbits/sec    0   1.85 MBytes
[  4]   7.00-8.01   sec   211 MBytes  1.76 Gbits/sec   11   1.37 MBytes
[  4]   8.01-9.00   sec   216 MBytes  1.82 Gbits/sec    0   1.47 MBytes
[  4]   9.00-10.00  sec   219 MBytes  1.84 Gbits/sec    0   1.55 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  1.94 GBytes  1.67 Gbits/sec   37             sender
[  4]   0.00-10.00  sec  1.94 GBytes  1.67 Gbits/sec                  receiver

iperf Done.

10 ポート番号を指定する方法(-p)

iperf3はデフォルトで5201番のポートを使います。すでに他のアプリが5201を使っている場合、5201以外のポート番号を使う必要があります。このような時に使用するオプションです。ここでは、5201番ポートではなく11111番ポートを使うため、TCPの11111番ポートを開放します。UDPを使って帯域測定する場合は、UDPの11111番ポートも開放してください。

[root@server ~]# firewall-cmd --add-port=11111/tcp
success

ポートの状態を確認します。TCPの11111番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
5201/tcp 5201/udp 11111/tcp

サーバでiperf3を実行します。このとき、-pオプションを指定して、iperf3がTCPの11111番ポートでListenするようにします。

[root@server ~]# iperf3 -s -p 11111
-----------------------------------------------------------
Server listening on 11111
-----------------------------------------------------------

もう1つターミナルを開きます。lsofコマンドを実行して、iperf3がListenしているポート番号を確認します。iperf3がTCPの11111番ポートでListenしていることがわかります。
なお、lsofコマンドの使い方は、
lsofコマンドの使い方 - hana_shin’s diaryを参照してください。

[root@server ~]# lsof -c iperf3 -a -i -a -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
iperf3  5392 root    3u  IPv6  73032      0t0  TCP *:11111 (LISTEN)

クライアントでiperf3コマンドを実行します。このとき、-pオプションを指定してサーバのiperf3がListenしているポート番号を指定します。

[root@client ~]# iperf3 -c 192.168.2.100 -p 11111
Connecting to host 192.168.2.100, port 11111
[  4] local 192.168.2.105 port 39192 connected to 192.168.2.100 port 11111
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec   145 MBytes  1.22 Gbits/sec    0    663 KBytes
[  4]   1.00-2.00   sec   191 MBytes  1.61 Gbits/sec    0   1.04 MBytes
[  4]   2.00-3.00   sec   192 MBytes  1.62 Gbits/sec   22   1.35 MBytes
[  4]   3.00-4.01   sec   178 MBytes  1.48 Gbits/sec    0   1.50 MBytes
[  4]   4.01-5.01   sec   189 MBytes  1.58 Gbits/sec    0   1.61 MBytes
[  4]   5.01-6.00   sec   216 MBytes  1.82 Gbits/sec    0   1.70 MBytes
[  4]   6.00-7.01   sec   208 MBytes  1.73 Gbits/sec    0   1.76 MBytes
[  4]   7.01-8.01   sec   219 MBytes  1.84 Gbits/sec    0   1.81 MBytes
[  4]   8.01-9.01   sec   221 MBytes  1.86 Gbits/sec    0   1.85 MBytes
[  4]   9.01-10.00  sec   216 MBytes  1.82 Gbits/sec    0   1.87 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  1.93 GBytes  1.66 Gbits/sec   22             sender
[  4]   0.00-10.00  sec  1.93 GBytes  1.65 Gbits/sec                  receiver

iperf Done.

11 指定したCPUでiperf3を実行する方法(-A)

iperf3を実行するCPUを指定するオプションです。このオプションは、サーバとクライアントで使うことができます。ここでは、サーバでiperf3を指定したCPUで動作させる実行例を示します。まず、サーバに搭載れているCPUを確認します。4つのCPUが搭載されていることがわかります。

[root@server ~]# cat /proc/cpuinfo |grep processor
processor       : 0
processor       : 1
processor       : 2
processor       : 3

iperf3をCPU0で動作させてみます。

[root@server ~]# iperf3 -s -A 0
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

psコマンドを実行して、iperf3が動作しているCPUを確認します。iperf3がCPU0で動作していることがわかります。

[root@server ~]# ps -C iperf3 -o comm,pid,psr
COMMAND            PID PSR
iperf3            5596   0

次にiperf3をCPU3で動作させてみます。

[root@server ~]#  iperf3 -s -A 3
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

psコマンドを実行して、iperf3が動作しているCPUを確認します。iperf3がCPU3で動作していることがわかります。

[root@server ~]# ps -C iperf3 -o comm,pid,psr
COMMAND            PID PSR
iperf3            5632   3

12 UDPを使う方法(-u)

帯域測定用のパケットにTCPではなくUDPを使う場合に指定するオプションです。サーバでiperf3コマンドを実行します。サーバ側はTCPでもUDPでもオプションに違いはありません。

12.1 デフォルトのビットレートで送信する方法

サーバでiperf3を実行します。

[root@server ~]#  iperf3 -s

クライアントでiperf3を実行します。このとき"-u"オプションを指定して、UDPパケットを送信してみます。デフォルトでは、1Mbits/secでUDPパケットを送信します。

[root@client ~]# iperf3 -c 192.168.2.100 -u
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 45835 connected to 192.168.2.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Total Datagrams
[  4]   0.00-1.00   sec   116 KBytes   950 Kbits/sec  82
[  4]   1.00-2.00   sec   129 KBytes  1.05 Mbits/sec  91
[  4]   2.00-3.00   sec   127 KBytes  1.04 Mbits/sec  90
[  4]   3.00-4.00   sec   129 KBytes  1.06 Mbits/sec  91
[  4]   4.00-5.00   sec   127 KBytes  1.04 Mbits/sec  90
[  4]   5.00-6.00   sec   129 KBytes  1.06 Mbits/sec  91
[  4]   6.00-7.00   sec   127 KBytes  1.04 Mbits/sec  90
[  4]   7.00-8.00   sec   129 KBytes  1.05 Mbits/sec  91
[  4]   8.00-9.00   sec   129 KBytes  1.05 Mbits/sec  91
[  4]   9.00-10.00  sec   127 KBytes  1.04 Mbits/sec  90
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Jitter    Lost/Total Datagrams
[  4]   0.00-10.00  sec  1.24 MBytes  1.04 Mbits/sec  0.137 ms  0/897 (0%)
[  4] Sent 897 datagrams

iperf Done.

12.2 ビットレートを指定する方法(-b)

"-b"を指定すると、UDPの送信ビットレートを指定することができます。ここでは、5Mbit/sでUDPパケットを送信してみます。

[root@client ~]# iperf3 -c 192.168.2.100 -u -b 5M
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 41786 connected to 192.168.2.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Total Datagrams
[  4]   0.00-1.00   sec   560 KBytes  4.57 Mbits/sec  396
[  4]   1.00-2.00   sec   604 KBytes  4.95 Mbits/sec  427
[  4]   2.00-3.00   sec   611 KBytes  5.01 Mbits/sec  432
[  4]   3.00-4.00   sec   609 KBytes  4.99 Mbits/sec  431
[  4]   4.00-5.00   sec   611 KBytes  5.01 Mbits/sec  432
[  4]   5.00-6.00   sec   609 KBytes  4.98 Mbits/sec  431
[  4]   6.00-7.00   sec   612 KBytes  5.01 Mbits/sec  433
[  4]   7.00-8.00   sec   609 KBytes  5.00 Mbits/sec  431
[  4]   8.00-9.00   sec   611 KBytes  5.00 Mbits/sec  432
[  4]   9.00-10.00  sec   608 KBytes  4.98 Mbits/sec  430
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Jitter    Lost/Total Datagrams
[  4]   0.00-10.00  sec  5.90 MBytes  4.95 Mbits/sec  0.090 ms  0/4275 (0%)
[  4] Sent 4275 datagrams

iperf Done.

13 データ送受信用コネクションを複数生成する方法(-P)

iperf3は、デフォルトで以下のTCPコネクションを生成します。
・制御用×1本(帯域測定にTCPを使うかUDPを使うか等の情報交換で使用する)
・帯域測定用×1本
"-P"オプションは帯域測定用のTCPコネクション数を指定します。ここでは、帯域測定用のTCPコネクションを3つ作成してみます。

サーバでiperf3コマンドを実行します。

[root@server ~]#  iperf3 -s

クライアントでiperf3コマンドを実行します。-Pオプションを指定して帯域測定用のTCPコネクションを3つ生成してみます。実行結果(★)より、TCPコネクションが3つ確立されていることがわかります。

[root@client ~]# iperf3 -c 192.168.2.100 -P 3
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 51172 connected to 192.168.2.100 port 5201 ★
[  6] local 192.168.2.105 port 51174 connected to 192.168.2.100 port 5201 ★
[  8] local 192.168.2.105 port 51176 connected to 192.168.2.100 port 5201 ★
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec  69.9 MBytes   586 Mbits/sec    0    553 KBytes
[  6]   0.00-1.00   sec  56.7 MBytes   476 Mbits/sec    0    477 KBytes
[  8]   0.00-1.00   sec  46.1 MBytes   386 Mbits/sec    0    411 KBytes
[SUM]   0.00-1.00   sec   173 MBytes  1.45 Gbits/sec    0
-snip-
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec   830 MBytes   696 Mbits/sec  125             sender
[  4]   0.00-10.00  sec   826 MBytes   693 Mbits/sec                  receiver
[  6]   0.00-10.00  sec   690 MBytes   579 Mbits/sec  109             sender
[  6]   0.00-10.00  sec   687 MBytes   576 Mbits/sec                  receiver
[  8]   0.00-10.00  sec   446 MBytes   374 Mbits/sec  120             sender
[  8]   0.00-10.00  sec   443 MBytes   372 Mbits/sec                  receiver
[SUM]   0.00-10.00  sec  1.92 GBytes  1.65 Gbits/sec  354             sender
[SUM]   0.00-10.00  sec  1.91 GBytes  1.64 Gbits/sec                  receiver

iperf Done.

14 IPv6で帯域測定をする方法(-6)

サーバでiperf3コマンドを実行します。このとき、-6オプションを指定してIPv6アドレスを使うことを指定します。

[[root@server ~]# iperf3 -s -6
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

クライアントでiperf3コマンドを実行します。インタフェース名は、帯域測定用のIPv6パケットを送信するインタフェースの名前です。<宛先のIPv6アドレス>%<インタフェース名>

[root@client ~]# iperf3 -6 -c fe80::174:936a:8876:8055%ens33
Connecting to host fe80::174:936a:8876:8055%ens33, port 5201
[  4] local fe80::3e2:a9c0:9fa6:a8ad port 48898 connected to fe80::174:936a:8876:8055 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec  95.7 MBytes   802 Mbits/sec    0    551 KBytes
[  4]   1.00-2.00   sec   131 MBytes  1.10 Gbits/sec    0    651 KBytes
[  4]   2.00-3.00   sec   158 MBytes  1.33 Gbits/sec    0    747 KBytes
[  4]   3.00-4.00   sec   173 MBytes  1.45 Gbits/sec    0   1.10 MBytes
[  4]   4.00-5.00   sec   173 MBytes  1.45 Gbits/sec    0   1.53 MBytes
[  4]   5.00-6.00   sec   175 MBytes  1.47 Gbits/sec    0   1.54 MBytes
[  4]   6.00-7.00   sec   176 MBytes  1.48 Gbits/sec    0   1.54 MBytes
[  4]   7.00-8.00   sec   176 MBytes  1.48 Gbits/sec    0   1.54 MBytes
[  4]   8.00-9.00   sec   176 MBytes  1.48 Gbits/sec    0   1.54 MBytes
[  4]   9.00-10.00  sec   176 MBytes  1.48 Gbits/sec    0   1.54 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-10.00  sec  1.57 GBytes  1.35 Gbits/sec    0             sender
[  4]   0.00-10.00  sec  1.57 GBytes  1.35 Gbits/sec                  receiver

iperf Done.

15 ファイル転送のボトルネックを調べる方法(-F)

  • "-F"オプションを指定しないと、クライアントのiperf3が送信したデータはサーバのiperf3で受信され、ストレージに書き込まず、廃棄されます。
  • "-F"オプションを指定すると、サーバのiperf3は受信したデータをストレージに書き込みます。

問題が発生しているシステムで、"-F"オプションを指定した場合と指定しない場合の実行結果を比較して、ストレージにボトルネックがあるかどうか絞り込みます。なお、manによると、送信したファイルサイズが元のファイルサイズと一致しない場合があるようです。

テスト用のファイルを作成します。
なお、ファイルの作成方法は、ファイルの作り方 - hana_shin’s diaryを参照してください。

[root@client ~]# fallocate -l 500M test.dat

作成したファイルのファイルサイズを確認します。

[root@client ~]# ls -lh test.dat
-rw-r--r--. 1 root root 500M  1月 16 11:10 test.dat

サーバでiperf3コマンドを実行します。このとき、-Fオプションを指定して受信したデータを書き込むファイルを指定します。

[root@server ~]# iperf3 -s -F test.dat
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

クライアントでiperf3コマンドを実行します。このとき、-Fオプションを指定して送信するファイルを指定します。

[root@client ~]# iperf3 -c 192.168.2.100 -F test.dat
Connecting to host 192.168.2.100, port 5201
[  4] local 192.168.2.105 port 46566 connected to 192.168.2.100 port 5201
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec  75.1 MBytes   630 Mbits/sec    0    212 KBytes
[  4]   1.00-2.00   sec   101 MBytes   847 Mbits/sec    0    298 KBytes
[  4]   2.00-2.43   sec  54.4 MBytes  1.05 Gbits/sec    0    328 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-2.43   sec   230 MBytes   794 Mbits/sec    0             sender
        Sent  230 MByte /  500 MByte (46%) of test.dat
[  4]   0.00-2.43   sec   227 MBytes   784 Mbits/sec                  receiver

iperf Done.

なお、サーバでstraceコマンドを使うと、iperf3が受信データをファイルに書き込んでいることがわかります。straceコマンドの使いかたは、straceコマンドの使い方 - hana_shin’s diaryを参照してください。

[root@server ~]# strace -e trace=open iperf3 -s -F test.dat
-snip-
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
open("/etc/localtime", O_RDONLY|O_CLOEXEC) = 5
Accepted connection from 192.168.2.105, port 46580
open("/tmp/iperf3.xTHBO9", O_RDWR|O_CREAT|O_EXCL, 0600) = 6
open("test.dat", O_WRONLY|O_CREAT|O_TRUNC, 0600) = 7
-snip-

16 詳細情報を表示する方法(-V)

このオプションは、サーバ、クライアントの両方で使用できます。Vオプションを指定すると、iperf3の版数、OS版数、実行時刻等の詳細情報が追加表示されます。

[root@server ~]# iperf3 -s -V
iperf 3.1.7
Linux server 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Time: Sat, 15 Jan 2022 12:50:06 GMT
Accepted connection from 192.168.2.105, port 51194
      Cookie: client.1642251006.468534.06949ef12fc
      TCP MSS: 0 (default)
-snip-
[root@client ~]# iperf3 -c 192.168.2.100 -V
iperf 3.1.7
Linux client 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
Control connection MSS 1448
Time: Sat, 15 Jan 2022 12:50:06 GMT
Connecting to host 192.168.2.100, port 5201
      Cookie: client.1642251006.468534.06949ef12fc
      TCP MSS: 1448 (default)
-snip-

17 タイトルを付ける方法(-T)

表示されるレポートの各行の行頭に文字列を表示することができます。ここではホスト名を表示してみます。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

hostnameコマンドの実行結果(client)が左端に表示されていることがわかります。

[root@client ~]# iperf3 -c 192.168.2.100 -T `hostname`
client:  Connecting to host 192.168.2.100, port 5201
client:  [  4] local 192.168.2.105 port 51200 connected to 192.168.2.100 port 5201
client:  [ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
client:  [  4]   0.00-1.00   sec   148 MBytes  1.24 Gbits/sec    0   1.33 MBytes
client:  [  4]   1.00-2.00   sec   185 MBytes  1.55 Gbits/sec   10   1.42 MBytes
client:  [  4]   2.00-3.00   sec   191 MBytes  1.61 Gbits/sec    0   1.55 MBytes
client:  [  4]   3.00-4.00   sec   200 MBytes  1.68 Gbits/sec    0   1.66 MBytes
client:  [  4]   4.00-5.00   sec   200 MBytes  1.68 Gbits/sec    0   1.74 MBytes
client:  [  4]   5.00-6.00   sec   214 MBytes  1.80 Gbits/sec    0   1.80 MBytes
client:  [  4]   6.00-7.00   sec   209 MBytes  1.75 Gbits/sec    0   1.84 MBytes
client:  [  4]   7.00-8.00   sec   220 MBytes  1.85 Gbits/sec    0   1.87 MBytes
client:  [  4]   8.00-9.00   sec   192 MBytes  1.61 Gbits/sec   19   1.37 MBytes
client:  [  4]   9.00-10.01  sec   221 MBytes  1.85 Gbits/sec    0   1.47 MBytes
client:  - - - - - - - - - - - - - - - - - - - - - - - - -
client:  [ ID] Interval           Transfer     Bandwidth       Retr
client:  [  4]   0.00-10.01  sec  1.93 GBytes  1.66 Gbits/sec   29             sender
client:  [  4]   0.00-10.01  sec  1.93 GBytes  1.66 Gbits/sec                  receiver
client:
client:  iperf Done.

TSO(TCP Segmentation Offload)について

1 TSO(TCP Segmentation Offload)とは?

TSOは、NICが送信パケットをMTU長に分割する仕組みです。以前は、カーネルで分割していましたが、最近はNICでパケットを分割することができます。NICでパケットを分割することで、カーネルの負荷を低減することができます。ここでは、仮想NICのTSOが有効/無効の時、TCPパケットがどのように分割されるかを確認してみます。

2 検証環境

2.1 ネットワーク構成

VMware Workstation 15 Player上の仮想マシンを使っています。サーバとクライアントの2台構成です。図中のens33はNICの名前です。

                          192.168.2.0/24
client(ens33) -------------------------------------(ens33) server
        .105                                       .100

2.2 版数

サーバ、クライアントともに下記版数です。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname  -r
3.10.0-1160.el7.x86_64

3 事前準備

サーバのTCP/11111番ポートを使用するので、TCP/11111番ポートへのアクセスを許可します。

[root@server ~]# firewall-cmd --add-port=11111/tcp
success

ポートの状態を確認します。11111番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
11111/tcp

4 TSO有効時

仮想NICのTSOを確認します。TSOが有効(★)であることがわかります。なお、指定するオプション-kは小文字です。

[root@client ~]# ethtool -k ens33 | grep tcp
tcp-segmentation-offload: on ★
        tx-tcp-segmentation: on
        tx-tcp-ecn-segmentation: off [fixed]
        tx-tcp6-segmentation: off [fixed]
        tx-tcp-mangleid-segmentation: off

クライアントからサーバに送信する送信データを作成します。データサイズは2000(byte)とします。送信データの作成方法は、ファイルの作り方 - hana_shin’s diaryを参照してください。

[root@client ~]# fallocate -l 2000 test.dat
[root@client ~]# ls -l test.dat
-rw-r--r--. 1 root root 2000  1月 14 21:25 test.dat

クライアントでtcpdumpを実行します。tcpdumpの使い方は、tcpdumpの使い方(基本編) - hana_shin’s diaryを参照してください。

[root@client ~]# tcpdump -i ens33 port 11111
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes

サーバでncコマンドを実行します。ncプロセスがTCPの11111番ポートでListenするようにします。ncコマンドは、ncコマンドの使い方 - hana_shin’s diaryを参照してください。

[root@server ~]# nc -klv 11111 > test.dat
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::11111
Ncat: Listening on 0.0.0.0:11111

ncプロセスがListenしているポート番号を確認します。TCPの11111番ポートでListenしていることがわかります。lsofコマンドは、lsofコマンドの使い方 - hana_shin’s diaryを参照してください。

[root@server ~]# lsof -c nc -a -i4 -a -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      3964 root    4u  IPv4  55566      0t0  TCP *:11111 (LISTEN)

クライアントからサーバにデータを送信します。

[root@client ~]# nc server 11111 < test.dat

tcpdumpの実行結果を確認します。tcpdumpは、カーネルNICの間でパケットをキャプチャします。TSOが有効なので、TCP層ではなくNICでパケットの分割をするので、TCPパケットのペイロードサイズが2000(byte)と表示されています。

[root@client ~]# tcpdump -i ens33 port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
21:32:08.742695 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [S], seq 3702008934, win 29200, options [mss 1460,sackOK,TS val 474675 ecr 0,nop,wscale 7], length 0
21:32:08.743483 IP 192.168.2.100.11111 > 192.168.2.105.57310: Flags [S.], seq 2914285991, ack 3702008935, win 28960, options [mss 1460,sackOK,TS val 9891545 ecr 474675,nop,wscale 7], length 0
21:32:08.743560 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 474676 ecr 9891545], length 0
21:32:08.743731 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [P.], seq 1:2001, ack 1, win 229, options [nop,nop,TS val 474676 ecr 9891545], length 2000 ★
21:32:08.743861 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [F.], seq 2001, ack 1, win 229, options [nop,nop,TS val 474676 ecr 9891545], length 0
21:32:08.744240 IP 192.168.2.100.11111 > 192.168.2.105.57310: Flags [.], ack 2001, win 258, options [nop,nop,TS val 9891546 ecr 474676], length 0
21:32:08.744287 IP 192.168.2.100.11111 > 192.168.2.105.57310: Flags [F.], seq 1, ack 2002, win 258, options [nop,nop,TS val 9891546 ecr 474676], length 0
21:32:08.744394 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [.], ack 2, win 229, options [nop,nop,TS val 474677 ecr 9891546], length 0

5 TSO無効時

ens33のTSOを無効にします。つまり、カーネルで送信パケットを分割することになります。なお、指定するオプション-Kは大文字です。

[root@client ~]# ethtool -K ens33 tso off

TSOの設定有無を確認します。TSOが無効(★)になったことがわかります。なお、指定するオプション-kは小文字です。

[root@client ~]# ethtool -k ens33|grep tcp
tcp-segmentation-offload: off ★
        tx-tcp-segmentation: off
        tx-tcp-ecn-segmentation: off [fixed]
        tx-tcp6-segmentation: off [fixed]
        tx-tcp-mangleid-segmentation: off

クライアントでtcpdumpを実行します。

[root@client ~]# tcpdump -i ens33 port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes

サーバでncコマンドを実行します。ncプロセスがTCPの11111番ポートでListenするようにします。

[root@server ~]# nc -klv 11111 > test.dat
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::11111
Ncat: Listening on 0.0.0.0:11111

クライアントからサーバにデータを送信します。

[root@client ~]# nc server 11111 < test.dat

tcpdumpの実行結果を確認します。tcpdumpは、カーネルNICの間でパケットをキャプチャします。TSOが無効なので、TCP層でパケットを分割をするので、TCPパケットのペイロードサイズが1448(byte)と552(byte)と表示されています。

[root@client ~]# tcpdump -i ens33 port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
21:36:20.217351 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [S], seq 3888141037, win 29200, options [mss 1460,sackOK,TS val 726136 ecr 0,nop,wscale 7], length 0
21:36:20.217964 IP 192.168.2.100.11111 > 192.168.2.105.57312: Flags [S.], seq 4173351009, ack 3888141038, win 28960, options [mss 1460,sackOK,TS val 10143024 ecr 726136,nop,wscale 7], length 0
21:36:20.218192 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 726150 ecr 10143024], length 0
21:36:20.218659 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [.], seq 1:1449, ack 1, win 229, options [nop,nop,TS val 726151 ecr 10143024], length 1448 ★
21:36:20.218666 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [P.], seq 1449:2001, ack 1, win 229, options [nop,nop,TS val 726151 ecr 10143024], length 552 ★
21:36:20.218673 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [F.], seq 2001, ack 1, win 229, options [nop,nop,TS val 726151 ecr 10143024], length 0
21:36:20.219287 IP 192.168.2.100.11111 > 192.168.2.105.57312: Flags [.], ack 2001, win 258, options [nop,nop,TS val 10143025 ecr 726151], length 0
21:36:20.219782 IP 192.168.2.100.11111 > 192.168.2.105.57312: Flags [F.], seq 1, ack 2002, win 258, options [nop,nop,TS val 10143025 ecr 726151], length 0
21:36:20.219874 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [.], ack 2, win 229, options [nop,nop,TS val 726152 ecr 10143025], length 0

6 まとめ

・TSOが有効の場合:NICでパケット分割
・TSOが無効の場合:カーネルでパケット分割

カーネルが分割するパケットサイズは、MTUからTCP/IPヘッダのサイズを引いた値になります。MTUはイーサネット環境では1500(byte)です。TCP/IPヘッダサイズは、各々20(byte)です。したがって、分割するパケットサイズは1460(byte)になります。しかし、最近はTCPヘッダに、TCPタイムスタンプオプション(12byte)が付くのが一般的です。したがって、分割するパケットサイズは、1448(byte)単位になります。なお、オプションヘッダを付けるかどうかは、3 way handshakeの時に決定します。TCPの両端がTCPオプションを付けることに合意すれば、TCPオプションが付加されます。

tracerouteコマンドの使い方

1 tracerouteコマンドとは?

あるホストから別ホストまでに経由するルータのインタフェースのIPアドレスを表示するコマンドです。

具体的に説明すると次のようになります。
host1で宛先をhost2にしてtracerouteコマンドを実行すると、eth2,eth4,eth6のIPアドレスが表示されます。なお、router1,router2はルータです。ethX(X=1,..,6)はNICを表します。

host1(eth1) ------ (eth2)router1(eth3) ------ (eth4)router2(eth5) ------ (eth6)host2

2 検証環境

2.1 CentrOS版数

VMware Workstation 15 Player上の仮想マシンを使っています。
CentOS版数は次のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

2.2 カーネル版数

カーネル版数は次のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

2.3 ネットワーク構成

検証環境のネットワーク構成は以下のとおりです。ネットワークネームスペースを使って、仮想マシンに下記のネットワークを構築しました。h1,h2はホスト、r1,r2はルータです。ネットワークは、下記のとおり3つあります。環境構築方法は別途記事を作成予定です。

 +-------------------------------- Guest Machine(CentOS7.9) ---------------------------------+
 |                                                                                           |
 |                                                                                           |
 |          192.168.100.0/24             192.168.110.0/24            192.168.120.0/24        |
 |                                                                                           |
 | +-- h1 --+             +----- r1 -----+            +----- r2 -----+            +-- h2 --+ |
 | |        |             |              |            |              |            |        | |
 | |   veth1-h1 -------- veth1-r1  veth2-r1 -------- veth1-r2  veth2-r2 -------- veth1-h2  | |
 | |        | .10     .20 |              | .10    .20 |              |.10     .20 |        | |
 | +--------+             +--------------+            +--------------+            +--------+ |
 |                                                                                           |
 |                                                                                           |
 +-------------------------------------------------------------------------------------------+

2.4 ホスト、ルータのインタフェース確認

ホスト1(h1)

ホスト1のインタフェースを確認します。

[root@server ~]# ip netns exec h1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
6: veth1-h1@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 32:6c:4f:92:07:7e brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 192.168.100.10/24 scope global veth1-h1
       valid_lft forever preferred_lft forever
    inet6 fe80::306c:4fff:fe92:77e/64 scope link
       valid_lft forever preferred_lft forever
ルータ1(r1)

ルータ1のインタフェースを確認します。

[root@server ~]# ip netns exec r1 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1-r1@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 46:c5:de:a4:ac:44 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.100.20/24 scope global veth1-r1
       valid_lft forever preferred_lft forever
    inet6 fe80::44c5:deff:fea4:ac44/64 scope link
       valid_lft forever preferred_lft forever
8: veth2-r1@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 5e:67:6b:ee:23:2c brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet 192.168.110.10/24 scope global veth2-r1
       valid_lft forever preferred_lft forever
    inet6 fe80::5c67:6bff:feee:232c/64 scope link
       valid_lft forever preferred_lft forever
ルータ2(r2)

ルータ2のインタフェースを確認します。

[root@server ~]# ip netns exec r2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
7: veth1-r2@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 16:2d:f8:47:7a:5a brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.110.20/24 scope global veth1-r2
       valid_lft forever preferred_lft forever
    inet6 fe80::142d:f8ff:fe47:7a5a/64 scope link
       valid_lft forever preferred_lft forever
10: veth2-r2@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether e6:3e:75:6f:4c:8e brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet 192.168.120.10/24 scope global veth2-r2
       valid_lft forever preferred_lft forever
    inet6 fe80::e43e:75ff:fe6f:4c8e/64 scope link
       valid_lft forever preferred_lft forever
ホスト2(h2)

ホスト2のインタフェースを確認します。

[root@server ~]# ip netns exec h2 ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
9: veth1-h2@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 4e:b3:e9:50:c8:f3 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.120.20/24 scope global veth1-h2
       valid_lft forever preferred_lft forever
    inet6 fe80::4cb3:e9ff:fe50:c8f3/64 scope link
       valid_lft forever preferred_lft forever

2.5 ルーティングテーブルの確認

ルータのルーティングテーブルを確認してみます。

ルータ1(r1)
[root@server ~]# ip netns exec r1 ip r
192.168.100.0/24 dev veth1-r1 proto kernel scope link src 192.168.100.20
192.168.110.0/24 dev veth2-r1 proto kernel scope link src 192.168.110.10
192.168.120.0/24 via 192.168.110.20 dev veth2-r1
ルータ2(r2)
[root@server ~]# ip netns exec r2 ip r
192.168.100.0/24 via 192.168.110.10 dev veth1-r2
192.168.110.0/24 dev veth1-r2 proto kernel scope link src 192.168.110.20
192.168.120.0/24 dev veth2-r2 proto kernel scope link src 192.168.120.10

3 インストール方法

tracerouteパッケージをインストールします。

[root@server ~]# yum -y install traceroute

tracerouteコマンドの版数を確認します。

[root@server ~]# traceroute -V
Modern traceroute for Linux, version 2.0.22
Copyright (c) 2008  Dmitry Butskoy,   License: GPL v2 or any later

4 オプション一覧

[root@server ~]# traceroute --help
Usage:
  traceroute [ -46dFITnreAUDV ] [ -f first_ttl ] [ -g gate,... ] [ -i device ] [ -m max_ttl ] [ -N squeries ] [ -p port ] [ -t tos ] [ -l flow_label ] [ -w waittime ] [ -q nqueries ] [ -s src_addr ] [ -z sendwait ] [ --fwmark=num ] host [ packetlen ]
Options:
  -4                          Use IPv4
  -6                          Use IPv6
  -d  --debug                 Enable socket level debugging
  -F  --dont-fragment         Do not fragment packets
  -f first_ttl  --first=first_ttl
                              Start from the first_ttl hop (instead from 1)
  -g gate,...  --gateway=gate,...
                              Route packets through the specified gateway
                              (maximum 8 for IPv4 and 127 for IPv6)
  -I  --icmp                  Use ICMP ECHO for tracerouting
  -T  --tcp                   Use TCP SYN for tracerouting (default port is 80)
  -i device  --interface=device
                              Specify a network interface to operate with
  -m max_ttl  --max-hops=max_ttl
                              Set the max number of hops (max TTL to be
                              reached). Default is 30
  -N squeries  --sim-queries=squeries
                              Set the number of probes to be tried
                              simultaneously (default is 16)
  -n                          Do not resolve IP addresses to their domain names
  -p port  --port=port        Set the destination port to use. It is either
                              initial udp port value for "default" method
                              (incremented by each probe, default is 33434), or
                              initial seq for "icmp" (incremented as well,
                              default from 1), or some constant destination
                              port for other methods (with default of 80 for
                              "tcp", 53 for "udp", etc.)
  -t tos  --tos=tos           Set the TOS (IPv4 type of service) or TC (IPv6
                              traffic class) value for outgoing packets
  -l flow_label  --flowlabel=flow_label
                              Use specified flow_label for IPv6 packets
  -w waittime  --wait=waittime
                              Set the number of seconds to wait for response to
                              a probe (default is 5.0). Non-integer (float
                              point) values allowed too
  -q nqueries  --queries=nqueries
                              Set the number of probes per each hop. Default is
                              3
  -r                          Bypass the normal routing and send directly to a
                              host on an attached network
  -s src_addr  --source=src_addr
                              Use source src_addr for outgoing packets
  -z sendwait  --sendwait=sendwait
                              Minimal time interval between probes (default 0).
                              If the value is more than 10, then it specifies a
                              number in milliseconds, else it is a number of
                              seconds (float point values allowed too)
  -e  --extensions            Show ICMP extensions (if present), including MPLS
  -A  --as-path-lookups       Perform AS path lookups in routing registries and
                              print results directly after the corresponding
                              addresses
  -M name  --module=name      Use specified module (either builtin or external)
                              for traceroute operations. Most methods have
                              their shortcuts (`-I' means `-M icmp' etc.)
  -O OPTS,...  --options=OPTS,...
                              Use module-specific option OPTS for the
                              traceroute module. Several OPTS allowed,
                              separated by comma. If OPTS is "help", print info
                              about available options
  --sport=num                 Use source port num for outgoing packets. Implies
                              `-N 1'
  --fwmark=num                Set firewall mark for outgoing packets
  -U  --udp                   Use UDP to particular port for tracerouting
                              (instead of increasing the port per each probe),
                              default port is 53
  -UL                         Use UDPLITE for tracerouting (default dest port
                              is 53)
  -D  --dccp                  Use DCCP Request for tracerouting (default port
                              is 33434)
  -P prot  --protocol=prot    Use raw packet of protocol prot for tracerouting
  --mtu                       Discover MTU along the path being traced. Implies
                              `-F -N 1'
  --back                      Guess the number of hops in the backward path and
                              print if it differs
  -V  --version               Print version info and exit
  --help                      Read this help and exit

Arguments:
+     host          The host to traceroute to
      packetlen     The full packet length (default is the length of an IP
                    header plus 40). Can be ignored or increased to a minimal
                    allowed value

5 検証環境における疎通確認

ホスト1からホスト2に対してpingを実行してみます。まず、ホストに1入ります。

[root@server ~]# ip netns exec h1 bash

ホスト1のインタフェースを確認します。

[root@server ~]# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
6: veth1-h1@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 32:6c:4f:92:07:7e brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 192.168.100.10/24 scope global veth1-h1
       valid_lft forever preferred_lft forever
    inet6 fe80::306c:4fff:fe92:77e/64 scope link
       valid_lft forever preferred_lft forever

ホスト2に対してpingを1回実行します。
なお、pingの使い方は、pingコマンドの使い方 - hana_shin’s diaryを参照してください。

[root@server ~]# ping -c 1 192.168.120.20
PING 192.168.120.20 (192.168.120.20) 56(84) bytes of data.
64 bytes from 192.168.120.20: icmp_seq=1 ttl=62 time=0.509 ms

--- 192.168.120.20 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.509/0.509/0.509/0.000 ms

このとき、ルータ2でtcpdumpを実行します。
以下のように、ホスト1からホスト2へのICMP echo requestパケット、ホスト2からホスト1へのICMP echo replyパケットが出力されていることがわかります。なお、tcpdumpの使い方は、tcpdumpの使い方(基本編) - hana_shin’s diaryを参照してください。

[root@server ~]# tcpdump -i veth2-r2 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth2-r2, link-type EN10MB (Ethernet), capture size 262144 bytes
21:42:32.513620 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 2517, seq 1, length 64
21:42:32.513683 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 2517, seq 1, length 64

最後に、ホスト2でtcpdumpを実行します。
以下のように、ホスト1からホスト2へのICMP echo requestパケット、ホスト2からホスト1へのICMP echo replyパケットが出力されていることがわかります。

[root@server ~]# tcpdump -i veth1-h2 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1-h2, link-type EN10MB (Ethernet), capture size 262144 bytes
21:48:40.394730 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 2626, seq 1, length 64
21:48:40.394756 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 2626, seq 1, length 64

6 基本的な使い方

宛先IPアドレスを指定してコマンドを実行します。ルータ1、ルータ2を経由してホスト2に到達していることがわかります。

[root@server ~]# traceroute 192.168.120.20
traceroute to 192.168.120.20 (192.168.120.20), 30 hops max, 60 byte packets
 1  192.168.100.20 (192.168.100.20)  0.075 ms  0.027 ms  0.017 ms
 2  192.168.110.20 (192.168.110.20)  0.040 ms  0.026 ms  0.026 ms
 3  192.168.120.20 (192.168.120.20)  0.048 ms  0.037 ms  0.037 ms

この時、tracerouteがどのようなパケットを使って経路を調べているのかを確認してみます。
ホスト2でtcpdumpコマンドを実行します。宛先に対してUDPパケットを送信していることがわかります。宛先ではUDPパケットの宛先で待ち受けるプロセスがいないので、ICMPのudp port unreachableメッセージが返信されていることがわかります。

[root@server ~]# tcpdump -i veth1-h2 -nn
22:02:42.520859 IP 192.168.100.10.49558 > 192.168.120.20.33440: UDP, length 32
22:02:42.520890 IP 192.168.120.20 > 192.168.100.10: ICMP 192.168.120.20 udp port 33440 unreachable, length 68
22:02:42.521056 IP 192.168.100.10.35204 > 192.168.120.20.33441: UDP, length 32
22:02:42.521092 IP 192.168.120.20 > 192.168.100.10: ICMP 192.168.120.20 udp port 33441 unreachable, length 68
22:02:42.521245 IP 192.168.100.10.47681 > 192.168.120.20.33442: UDP, length 32
22:02:42.521290 IP 192.168.120.20 > 192.168.100.10: ICMP 192.168.120.20 udp port 33442 unreachable, length 68
22:02:42.521338 IP 192.168.100.10.39878 > 192.168.120.20.33443: UDP, length 32
22:02:42.521348 IP 192.168.120.20 > 192.168.100.10: ICMP 192.168.120.20 udp port 33443 unreachable, length 68
22:02:42.521406 IP 192.168.100.10.44577 > 192.168.120.20.33444: UDP, length 32
22:02:42.521423 IP 192.168.120.20 > 192.168.100.10: ICMP 192.168.120.20 udp port 33444 unreachable, length 68
22:02:42.521483 IP 192.168.100.10.59295 > 192.168.120.20.33445: UDP, length 32
22:02:42.521494 IP 192.168.120.20 > 192.168.100.10: ICMP 192.168.120.20 udp port 33445 unreachable, length 68
22:02:42.521551 IP 192.168.100.10.56743 > 192.168.120.20.33446: UDP, length 32
22:02:42.521655 IP 192.168.100.10.37366 > 192.168.120.20.33447: UDP, length 32
22:02:42.521755 IP 192.168.100.10.35610 > 192.168.120.20.33448: UDP, length 32
22:02:42.521808 IP 192.168.100.10.59687 > 192.168.120.20.33449: UDP, length 32

7 ICMPパケットを使って経路を調べる方法(-I)

[root@server ~]# traceroute -I 192.168.120.20
traceroute to 192.168.120.20 (192.168.120.20), 30 hops max, 60 byte packets
 1  192.168.100.20 (192.168.100.20)  0.062 ms  0.013 ms  0.009 ms
 2  192.168.110.20 (192.168.110.20)  0.075 ms  0.021 ms  0.017 ms
 3  192.168.120.20 (192.168.120.20)  0.032 ms  0.020 ms  0.019 ms

ホスト2でtcpdumpコマンドを実行します。ICMPパケットを使っていることがわかります。

[root@server ~]# tcpdump -i veth1-h2 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1-h2, link-type EN10MB (Ethernet), capture size 262144 bytes
22:24:42.625108 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 7, length 40
22:24:42.625135 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 7, length 40
22:24:42.625173 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 8, length 40
22:24:42.625183 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 8, length 40
22:24:42.625218 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 9, length 40
22:24:42.625227 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 9, length 40
22:24:42.625260 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 10, length 40
22:24:42.625271 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 10, length 40
22:24:42.625300 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 11, length 40
22:24:42.625309 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 11, length 40
22:24:42.625342 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 12, length 40
22:24:42.625351 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 12, length 40
22:24:42.625387 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 13, length 40
22:24:42.625473 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 13, length 40
22:24:42.625534 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 14, length 40
22:24:42.625546 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 14, length 40
22:24:42.625580 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 15, length 40
22:24:42.625590 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 15, length 40
22:24:42.625626 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 16, length 40
22:24:42.625637 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 16, length 40
22:24:42.628560 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 17, length 40
22:24:42.628616 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 17, length 40
22:24:42.628695 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 18, length 40
22:24:42.628711 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 18, length 40
22:24:42.628763 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 19, length 40
22:24:42.628774 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 19, length 40
22:24:42.629134 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 20, length 40
22:24:42.629201 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 20, length 40
22:24:42.629273 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 21, length 40
22:24:42.629284 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 21, length 40
22:24:42.629334 IP 192.168.100.10 > 192.168.120.20: ICMP echo request, id 3097, seq 22, length 40
22:24:42.629351 IP 192.168.120.20 > 192.168.100.10: ICMP echo reply, id 3097, seq 22, length 40

8 TCPパケットを使って経路を調べる方法(-T)

8.1 デフォルトの場合

[root@server ~]# traceroute -T 192.168.120.20
traceroute to 192.168.120.20 (192.168.120.20), 30 hops max, 44 byte packets
 1  192.168.100.20 (192.168.100.20)  0.067 ms  0.021 ms  0.017 ms
 2  192.168.110.20 (192.168.110.20)  0.037 ms  0.026 ms  0.025 ms
 3  192.168.120.20 (192.168.120.20)  0.060 ms  0.042 ms  0.041 ms

ホスト2でtcpdumpコマンドを実行します。TCPパケットを使っていることがわかります。宛先ポート番号は80番を使用していることがわかります。

[root@server ~]# tcpdump -i veth1-h2 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1-h2, link-type EN10MB (Ethernet), capture size 262144 bytes
22:27:34.775300 IP 192.168.100.10.39005 > 192.168.120.20.80: Flags [S], seq 79980328, win 5840, options [mss 1460], length 0
22:27:34.775326 IP 192.168.120.20.80 > 192.168.100.10.39005: Flags [R.], seq 0, ack 79980329, win 0, length 0
22:27:34.775387 IP 192.168.100.10.47157 > 192.168.120.20.80: Flags [S], seq 232901152, win 5840, options [mss 1460], length 0
22:27:34.775398 IP 192.168.120.20.80 > 192.168.100.10.47157: Flags [R.], seq 0, ack 232901153, win 0, length 0
22:27:34.775453 IP 192.168.100.10.58819 > 192.168.120.20.80: Flags [S], seq 2903372801, win 5840, options [mss 1460], length 0
22:27:34.775463 IP 192.168.120.20.80 > 192.168.100.10.58819: Flags [R.], seq 0, ack 2903372802, win 0, length 0
22:27:34.775520 IP 192.168.100.10.47148 > 192.168.120.20.80: Flags [S], seq 2462323147, win 5840, options [mss 1460], length 0
22:27:34.775531 IP 192.168.120.20.80 > 192.168.100.10.47148: Flags [R.], seq 0, ack 2462323148, win 0, length 0
22:27:34.775669 IP 192.168.100.10.45926 > 192.168.120.20.80: Flags [S], seq 3788273711, win 5840, options [mss 1460], length 0
22:27:34.775684 IP 192.168.120.20.80 > 192.168.100.10.45926: Flags [R.], seq 0, ack 3788273712, win 0, length 0
22:27:34.775760 IP 192.168.100.10.55775 > 192.168.120.20.80: Flags [S], seq 398808910, win 5840, options [mss 1460], length 0
22:27:34.775771 IP 192.168.120.20.80 > 192.168.100.10.55775: Flags [R.], seq 0, ack 398808911, win 0, length 0
22:27:34.775828 IP 192.168.100.10.54707 > 192.168.120.20.80: Flags [S], seq 3821878488, win 5840, options [mss 1460], length 0
22:27:34.775839 IP 192.168.120.20.80 > 192.168.100.10.54707: Flags [R.], seq 0, ack 3821878489, win 0, length 0
22:27:34.775894 IP 192.168.100.10.46160 > 192.168.120.20.80: Flags [S], seq 3832007428, win 5840, options [mss 1460], length 0
22:27:34.775906 IP 192.168.120.20.80 > 192.168.100.10.46160: Flags [R.], seq 0, ack 3832007429, win 0, length 0
22:27:34.776049 IP 192.168.100.10.39278 > 192.168.120.20.80: Flags [S], seq 734754141, win 5840, options [mss 1460], length 0
22:27:34.776081 IP 192.168.120.20.80 > 192.168.100.10.39278: Flags [R.], seq 0, ack 734754142, win 0, length 0
22:27:34.776158 IP 192.168.100.10.50904 > 192.168.120.20.80: Flags [S], seq 806679065, win 5840, options [mss 1460], length 0
22:27:34.776169 IP 192.168.120.20.80 > 192.168.100.10.50904: Flags [R.], seq 0, ack 806679066, win 0, length 0
22:27:34.779034 IP 192.168.100.10.58833 > 192.168.120.20.80: Flags [S], seq 1474654073, win 5840, options [mss 1460], length 0
22:27:34.779102 IP 192.168.120.20.80 > 192.168.100.10.58833: Flags [R.], seq 0, ack 1474654074, win 0, length 0
22:27:34.779281 IP 192.168.100.10.45807 > 192.168.120.20.80: Flags [S], seq 3609982926, win 5840, options [mss 1460], length 0
22:27:34.779327 IP 192.168.120.20.80 > 192.168.100.10.45807: Flags [R.], seq 0, ack 3609982927, win 0, length 0
22:27:34.779434 IP 192.168.100.10.50206 > 192.168.120.20.80: Flags [S], seq 736826700, win 5840, options [mss 1460], length 0
22:27:34.779479 IP 192.168.120.20.80 > 192.168.100.10.50206: Flags [R.], seq 0, ack 736826701, win 0, length 0
22:27:34.779946 IP 192.168.100.10.44705 > 192.168.120.20.80: Flags [S], seq 247997692, win 5840, options [mss 1460], length 0
22:27:34.780008 IP 192.168.120.20.80 > 192.168.100.10.44705: Flags [R.], seq 0, ack 247997693, win 0, length 0
22:27:34.780120 IP 192.168.100.10.40346 > 192.168.120.20.80: Flags [S], seq 873151949, win 5840, options [mss 1460], length 0
22:27:34.780136 IP 192.168.120.20.80 > 192.168.100.10.40346: Flags [R.], seq 0, ack 873151950, win 0, length 0
22:27:34.780219 IP 192.168.100.10.38559 > 192.168.120.20.80: Flags [S], seq 2001132989, win 5840, options [mss 1460], length 0
22:27:34.780263 IP 192.168.120.20.80 > 192.168.100.10.38559: Flags [R.], seq 0, ack 2001132990, win 0, length 0

8.2 宛先ポート番号を指定する方法(-p)

宛先ポート番号に11111番を指定してみます。

[root@server ~]# traceroute -T -p 11111 192.168.120.20
traceroute to 192.168.120.20 (192.168.120.20), 30 hops max, 44 byte packets
 1  192.168.100.20 (192.168.100.20)  0.066 ms  0.020 ms  0.017 ms
 2  192.168.110.20 (192.168.110.20)  0.037 ms  0.027 ms  0.027 ms
 3  192.168.120.20 (192.168.120.20)  0.196 ms  0.079 ms  0.120 ms

ホスト2でtcpdumpコマンドを実行します。TCPパケットを使っていることがわかります。宛先ポート番号は11111番を使用していることがわかります。

[root@server ~]# tcpdump -i veth1-h2 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth1-h2, link-type EN10MB (Ethernet), capture size 262144 bytes
22:33:40.187374 IP 192.168.100.10.44234 > 192.168.120.20.11111: Flags [S], seq 2213272040, win 5840, options [mss 1460], length 0
22:33:40.187452 IP 192.168.120.20.11111 > 192.168.100.10.44234: Flags [R.], seq 0, ack 2213272041, win 0, length 0
22:33:40.187586 IP 192.168.100.10.40588 > 192.168.120.20.11111: Flags [S], seq 3008821159, win 5840, options [mss 1460], length 0
22:33:40.187604 IP 192.168.120.20.11111 > 192.168.100.10.40588: Flags [R.], seq 0, ack 3008821160, win 0, length 0
22:33:40.187697 IP 192.168.100.10.45995 > 192.168.120.20.11111: Flags [S], seq 2403028551, win 5840, options [mss 1460], length 0
22:33:40.187759 IP 192.168.120.20.11111 > 192.168.100.10.45995: Flags [R.], seq 0, ack 2403028552, win 0, length 0
22:33:40.187875 IP 192.168.100.10.49749 > 192.168.120.20.11111: Flags [S], seq 613838244, win 5840, options [mss 1460], length 0
22:33:40.187894 IP 192.168.120.20.11111 > 192.168.100.10.49749: Flags [R.], seq 0, ack 613838245, win 0, length 0
22:33:40.187997 IP 192.168.100.10.60200 > 192.168.120.20.11111: Flags [S], seq 1531172490, win 5840, options [mss 1460], length 0
22:33:40.188015 IP 192.168.120.20.11111 > 192.168.100.10.60200: Flags [R.], seq 0, ack 1531172491, win 0, length 0
22:33:40.188112 IP 192.168.100.10.44440 > 192.168.120.20.11111: Flags [S], seq 3837802404, win 5840, options [mss 1460], length 0
22:33:40.188135 IP 192.168.120.20.11111 > 192.168.100.10.44440: Flags [R.], seq 0, ack 3837802405, win 0, length 0
22:33:40.188240 IP 192.168.100.10.43531 > 192.168.120.20.11111: Flags [S], seq 3355752251, win 5840, options [mss 1460], length 0
22:33:40.188262 IP 192.168.120.20.11111 > 192.168.100.10.43531: Flags [R.], seq 0, ack 3355752252, win 0, length 0
22:33:40.188431 IP 192.168.100.10.50479 > 192.168.120.20.11111: Flags [S], seq 2990302939, win 5840, options [mss 1460], length 0
22:33:40.188489 IP 192.168.120.20.11111 > 192.168.100.10.50479: Flags [R.], seq 0, ack 2990302940, win 0, length 0
22:33:40.188610 IP 192.168.100.10.47772 > 192.168.120.20.11111: Flags [S], seq 3782716177, win 5840, options [mss 1460], length 0
22:33:40.188631 IP 192.168.120.20.11111 > 192.168.100.10.47772: Flags [R.], seq 0, ack 3782716178, win 0, length 0
22:33:40.188742 IP 192.168.100.10.38857 > 192.168.120.20.11111: Flags [S], seq 4241160201, win 5840, options [mss 1460], length 0
22:33:40.188762 IP 192.168.120.20.11111 > 192.168.100.10.38857: Flags [R.], seq 0, ack 4241160202, win 0, length 0
22:33:40.191775 IP 192.168.100.10.33212 > 192.168.120.20.11111: Flags [S], seq 1696655026, win 5840, options [mss 1460], length 0
22:33:40.191830 IP 192.168.120.20.11111 > 192.168.100.10.33212: Flags [R.], seq 0, ack 1696655027, win 0, length 0
22:33:40.191948 IP 192.168.100.10.49816 > 192.168.120.20.11111: Flags [S], seq 2405122711, win 5840, options [mss 1460], length 0
22:33:40.191960 IP 192.168.120.20.11111 > 192.168.100.10.49816: Flags [R.], seq 0, ack 2405122712, win 0, length 0
22:33:40.192051 IP 192.168.100.10.41431 > 192.168.120.20.11111: Flags [S], seq 4036894686, win 5840, options [mss 1460], length 0
22:33:40.192063 IP 192.168.120.20.11111 > 192.168.100.10.41431: Flags [R.], seq 0, ack 4036894687, win 0, length 0
22:33:40.192742 IP 192.168.100.10.51965 > 192.168.120.20.11111: Flags [S], seq 2179451662, win 5840, options [mss 1460], length 0
22:33:40.192790 IP 192.168.120.20.11111 > 192.168.100.10.51965: Flags [R.], seq 0, ack 2179451663, win 0, length 0
22:33:40.192899 IP 192.168.100.10.45209 > 192.168.120.20.11111: Flags [S], seq 47755688, win 5840, options [mss 1460], length 0
22:33:40.192910 IP 192.168.120.20.11111 > 192.168.100.10.45209: Flags [R.], seq 0, ack 47755689, win 0, length 0
22:33:40.192991 IP 192.168.100.10.43143 > 192.168.120.20.11111: Flags [S], seq 792286855, win 5840, options [mss 1460], length 0
22:33:40.193002 IP 192.168.120.20.11111 > 192.168.100.10.43143: Flags [R.], seq 0, ack 792286856, win 0, length 0

9 インタフェースを指定する方法(-i)

インタフェースが複数あるホストの場合、どのインタフェースからtracerouteコマンドのパケットを送信するかを決める必要があります。このようなときに使用するオプションです。

9.1 ルータ1の実行結果

ルータ1でtracerouteコマンドを実行します。このとき、宛先をホスト2、インタフェースをveth2-r1としてみます。

[root@server ~]# ip netns exec r1 bash

ルータ2のNICIPアドレス(192.168.110.20)、ホスト2のNICIPアドレス(192.168.120.20)が出力されていることがわかります。

[root@server ~]# traceroute -i veth2-r1 192.168.120.20
traceroute to 192.168.120.20 (192.168.120.20), 30 hops max, 60 byte packets
 1  192.168.110.20 (192.168.110.20)  0.066 ms  0.020 ms  0.017 ms
 2  192.168.120.20 (192.168.120.20)  0.039 ms  0.029 ms  0.027 ms

9.2 ルータ2の実行結果

ルータ2でtracerouteコマンドを実行します。このとき、宛先をホスト1、インタフェースをveth1-r2としてみます。

[root@server ~]# ip netns exec r2 bash

ルータ1のNICIPアドレス(192.168.110.10)、ホスト1のNICIPアドレス(192.168.100.10)が出力されていることがわかります。

setsockopt SO_BINDTODEVICE: そのようなデバイスはありません
[root@server ~]# traceroute -i veth1-r2 192.168.100.10
traceroute to 192.168.100.10 (192.168.100.10), 30 hops max, 60 byte packets
 1  192.168.110.10 (192.168.110.10)  0.061 ms  0.021 ms  0.018 ms
 2  192.168.100.10 (192.168.100.10)  0.065 ms  0.030 ms  0.029 ms

10 経路上の最小MTUを調べる方法(--mtu)

mtuオプションを使うと、宛先までの経路上で最小のMTUを調べることができます。mtuオプションを使う場合、-F,-Nオプションも同時に指定します。

10.1 MTUが1500の場合

ネットワーク構成は以下のとおりです。全てのNICでMTUが1500です。

          192.168.100.0/24             192.168.110.0/24           192.168.120.0/24
            MTU=1500                       MTU=1500                    MTU=1500
   h1 ------------------------ r1 ------------------------ r2 ------------------------ h2
   (veth1-h1)        (veth1-r1)  (veth2-r1)      (veth1-r2)   (veth2-r2)     (veth1-h2)
     .10                    .20   .10                   .20   .10                   .20

ホスト1でtracerouteコマンドを実行します。

[root@server ~]# ip netns exec h1 bash
[root@server ~]# traceroute -N 1 -q 1 -F --mtu -I 192.168.120.20
traceroute to 192.168.120.20 (192.168.120.20), 30 hops max, 65000 byte packets
 1  192.168.100.20 (192.168.100.20)  0.236 ms F=1500
 2  192.168.110.20 (192.168.110.20)  0.082 ms
 3  192.168.120.20 (192.168.120.20)  0.127 ms

10.2 MTUが1000の場合

ルータ1で下記コマンドを実行して、veth2-r1のMTUを1000に変更します。

[root@server ~]# ip netns exec r1 bash
[root@server ~]# ip link set veth2-r1 mtu 1000
[root@server ~]# ip l show dev veth2-r1
8: veth2-r1@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1000 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 82:0f:63:07:a7:1f brd ff:ff:ff:ff:ff:ff link-netnsid 2

ルータ2で下記コマンドを実行して、 veth1-r2のMTUを1000に変更します。

[root@server ~]# ip netns exec r2 bash
[root@server ~]# ip link set veth1-r2 mtu 1000
[root@server ~]# ip l show dev veth1-r2
7: veth1-r2@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1000 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether de:82:ab:03:1d:9c brd ff:ff:ff:ff:ff:ff link-netnsid 0

設定変更後のネットワーク構成は以下のようになります。ルータ1とルータ2の間のMTUが1500から500になります。

          192.168.100.0/24             192.168.110.0/24           192.168.120.0/24
            MTU=1500                       MTU=1000                    MTU=1500
   h1 ------------------------ r1 ------------------------ r2 ------------------------ h2
   (veth1-h1)        (veth1-r1)  (veth2-r1)      (veth1-r2)   (veth2-r2)     (veth1-h2)
     .10                    .20   .10                   .20   .10                   .20

ホスト1でtracerouteコマンドを実行します。ルータ2のveth1-r2のMTUが1000であることがわかります。

[root@server ~]# ip netns exec h1 bash
[root@server ~]# traceroute -N 1 -q 1 -F --mtu -I 192.168.120.20
traceroute to 192.168.120.20 (192.168.120.20), 30 hops max, 65000 byte packets
 1  192.168.100.20 (192.168.100.20)  0.065 ms F=1500
 2  192.168.110.20 (192.168.110.20)  0.044 ms F=1000
 3  192.168.120.20 (192.168.120.20)  0.058 ms

10.3 MTUが500の場合

さらに、ルータ2とホスト2の間のMTUを1500から500に変更してみます。
ルータ2で下記コマンドを実行して、veth2-r2のMTUを500に変更します。

[root@server ~]# ip netns exec r2 bash
[root@server ~]# ip link set veth2-r2 mtu 500
[root@server ~]# ip l show dev veth2-r2
10: veth2-r2@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether ba:3d:19:b3:2d:62 brd ff:ff:ff:ff:ff:ff link-netnsid 2

ホスト2で下記コマンドを実行して、 veth1-h2のMTUを500に変更します。

[root@server ~]# ip netns exec h2 bash
[root@server ~]# ip link set veth1-h2 mtu 500
[root@server ~]# ip l show dev veth1-h2
9: veth1-h2@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether ee:0e:80:15:4d:2e brd ff:ff:ff:ff:ff:ff link-netnsid 0

設定変更後のネットワーク構成は以下のようになります。ルータ2とホスト2の間のMTUが1500から500になります

          192.168.100.0/24             192.168.110.0/24           192.168.120.0/24
            MTU=1500                       MTU=1000                    MTU=500
   h1 ------------------------ r1 ------------------------ r2 ------------------------ h2
   (veth1-h1)        (veth1-r1)  (veth2-r1)      (veth1-r2)   (veth2-r2)     (veth1-h2)
     .10                    .20   .10                   .20   .10                   .20

ホスト1でtracerouteコマンドを実行します。ルータ1のveth1-r1のMTUが1500、ルータ2のveth1-r2のMTUが1000、ホスト2のveth1-h2のMTUが500であることがわかります。

[root@server ~]# ip netns exec h1 bash
[root@server ~]# traceroute -N 1 -q 1 -I -F --mtu 192.168.120.20
traceroute to 192.168.120.20 (192.168.120.20), 30 hops max, 65000 byte packets
 1  192.168.100.20 (192.168.100.20)  0.055 ms F=1500
 2  192.168.110.20 (192.168.110.20)  0.056 ms F=1000
 3  192.168.120.20 (192.168.120.20)  0.070 ms F=500

なお、pingコマンドでも、同様に経路上の最小MTUを求めることができます。
pingコマンドの使い方は、pingコマンドの使い方 - hana_shin’s diaryを参照してください。

Netlinkプログラミングの書き方

1 Netlinkとは?

Netlinkとは、ユーザー空間とカーネル空間で情報をやりとりするためのインタフェースです。以下は、ユーザプロセスとカーネルモジュールがNetlinkインタフェースを使ってやり取りをしているところを図示したものです。

ユーザ空間
              +-----------------------+
              |                       |
              |     ユーザプロセス    |
              |                       |
              +-----------------------+
                          ↑
                          |
--------------------------|------------ Netlinkインタフェース
カーネル空間              |
                          ↓
              +-----------------------+
              |                       |
              |   カーネルモジュール  |
              |                       |
              +-----------------------+

Netlinkを使ってメッセージを送信するには、socketシステムコールの第1パラメータにAF_NETLINKを指定します。Netlinkを使っているユーザプロセスには、ipコマンドやdbusデーモン等があります。たとえば、ipコマンドを実行すると、socketシステムコールの第1パラメータにAF_NETLINKを指定していることがわかります。

[root@server ~]# strace -e trace=socket ip l
socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE) = 3

なお、straceコマンドの使い方は、straceコマンドの使い方 - hana_shin’s diaryを参照してください。

2. 検証環境

VMware Workstation 15 Player上の仮想マシンを使っています。
CentOS版数は次のとおりです。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は次のとおりです。

[root@server ~]# uname -r
3.10.0-1160.el7.x86_64

3 サンプルプログラムの作成

3.1 カーネルモジュールの作成

カーネルモジュールを作成します。カーネルジーュルは、カーネル空間で動作するプロセスです。ユーザ空間のプロセスが送信するメッセージを受信するカーネルモジュールを作成します。netlink_kernel_createの第2パラメータは、include/uapi/linux/netlink.hで未定義な値(17)を使いました。

[root@server ~]# cat netlink_module.c
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/init.h>
#include <net/sock.h>

#define NETLINK_TEST 17

static struct sock *nl_sock;
char *msg="kernel message";

static void nl_recv_msg (struct sk_buff *skb_in) {
    struct nlmsghdr *nlh;
    struct sk_buff *skb_out;
    int msg_size,pid;

    // receiving from user process
    nlh = (struct nlmsghdr *)skb_in->data;
    pid = nlh->nlmsg_pid;
    printk(KERN_INFO "received netlink message(%s) from PID(%d)\n", (char*)nlmsg_data(nlh), pid);

    // sending to user process
    msg_size=strlen(msg);
    skb_out = nlmsg_new(msg_size, 0);
    nlh=nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
    NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */
    strncpy(nlmsg_data(nlh), msg, msg_size);
    nlmsg_unicast(nl_sock, skb_out, pid);
}

struct netlink_kernel_cfg cfg = {
    .input = nl_recv_msg,
};

static int __init netlink_module_init(void)
{
  printk(KERN_INFO "NETLINK TEST START\n");
  nl_sock = netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
  return 0;
}

static void __exit netlink_module_exit(void)
{
  printk(KERN_INFO "NETLINK TEST FINISH\n");
  netlink_kernel_release(nl_sock);
}

module_init(netlink_module_init);
module_exit(netlink_module_exit);

MODULE_AUTHOR("hana_shin");
MODULE_LICENSE("GPL v2");


次に、Makefileを作成します。$(MAKE)やrmの前はスペースではなくTABです。

[root@server ~]# cat Makefile
obj-m := netlink_module.o

KDIR    := /lib/modules/$(shell uname -r)/build
VERBOSE = 0

all:
        $(MAKE) -C $(KDIR) M=$(PWD) KBUILD_VERBOSE=$(VERBOSE) modules
clean:
        rm -f *.o *.ko *.mod.c Module.symvers modules.order

makeコマンドを実行して、カーネルモジュールを作成します。

[root@server ~]# make
make -C /lib/modules/3.10.0-1160.el7.x86_64/build M=/root KBUILD_VERBOSE=0 modules
make[1]: ディレクトリ `/usr/src/kernels/3.10.0-1160.el7.x86_64' に入ります
  CC [M]  /root/netlink_module.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /root/netlink_module.mod.o
  LD [M]  /root/netlink_module.ko
make[1]: ディレクトリ `/usr/src/kernels/3.10.0-1160.el7.x86_64' から出ます

作成したカーネルモジュールを確認します。netlink_module.koが作成されていることがわかります。

[root@server ~]# ls -l netlink_module.*
-rw-r--r--. 1 root root   1312  1月 10 22:27 netlink_module.c
-rw-r--r--. 1 root root 413968  1月 10 22:30 netlink_module.ko
-rw-r--r--. 1 root root   1337  1月 10 22:30 netlink_module.mod.c
-rw-r--r--. 1 root root  59384  1月 10 22:30 netlink_module.mod.o
-rw-r--r--. 1 root root 358480  1月 10 22:30 netlink_module.o

3.2 ユーザプログラムの作成

ユーザ空間で動作するプログラムを作成します。

[root@server ~]# cat netlink_user.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>

#define NETLINK_USER 17
#define MAX_PAYLOAD 1024

struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;

int main(int argc, char *argv[])
{
    sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();
    bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));

    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;
    dest_addr.nl_groups = 0;

    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = getpid();
    nlh->nlmsg_flags = 0;

    strcpy(NLMSG_DATA(nlh), argv[1]);

    iov.iov_base = (void *)nlh;
    iov.iov_len = nlh->nlmsg_len;
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    printf("My PID is %d\n", getpid());
    printf("Sending message to kernel: %s\n", argv[1]);

    /* Send message to kernel */
    sendmsg(sock_fd,&msg,0);
    printf("Waiting for message from kernel\n");

    /* Read message from kernel */
    recvmsg(sock_fd, &msg, 0);
    printf("Received message from kernel: %s\n", (char *)NLMSG_DATA(nlh));
    close(sock_fd);
    return 0;
}

コンパイルします。

[root@server ~]# gcc -Wall -o netlink_user netlink_user.c

実行ファイルを確認します。

[root@server ~]# ls netlink_user*
netlink_user  netlink_user.c

4 動作確認

サンプルプログラムが出力するログを確認するため、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

カーネルモジュールをロードします。

[root@server ~]# insmod netlink_module.ko

ロードしたカーネルモジュールを確認します。作成したカーネルモジュールがロードされていることがわかります。

[root@server ~]# lsmod |grep netlink_module
netlink_module         12558  0

任意の文字列(11111)を指定してサンプルプログラム(netlink_user)を実行します。ユーザプロセスのPIDは4524であることがわかります。また、カーネルに"11111"を送信し、カーネルから"kernel message"というメッセージを受信していることがわかります。

[root@server ~]# ./netlink_user 11111
My PID is 4524
Sending message to kernel: 11111
Waiting for message from kernel
Received message from kernel: kernel message

カーネルモジュールが、PID=4524のユーザプロセスからメッセージを受信していることがわかります。

[root@server ~]# journalctl -f
-snip-
 1月 10 22:38:19 server kernel: NETLINK TEST START
 1月 10 22:39:47 server kernel: received netlink message(11111) from PID(4524)

後始末をします。netlink_moduleをアンロードします。

[root@server ~]# rmmod netlink_module

モジュールを確認します。netlink_moduleがアンロードされたことがわかります。

[root@server ~]# lsmod |grep netlink_module
[root@server ~]#

5 Netlinkメッセージ形式

ユーザプロセスからカーネルモジュールに送信される Netlinkメッセージを以下に示します。

                            dest_addr    sockaddr_nl
                               +----> +---------------+
                               |      | nl_family     | <= AF_NETLINKを設定する。
                               |      +---------------+
                               |      | nl_pad        |
                               |      +---------------+
             msghdr            |      | nl_pid        | <= プロセスのPID(getpid)を設定する。
msg -> +----------------+      |      +---------------+
       | *msg_name      | -----+      | nl_groups     | <= ユニキャスト通信なので0を設定する。
       +----------------+             +---------------+
       | msg_namelen(*1)|
       +----------------+                  iovec
       | *msg_iov       | ----------> +---------------+      nlh  |<- nlmsghdr ->|
       +----------------+             | *iov_base     | --------> +--------------+-----------------------+
       | msg_iovlen(*2) |             +---------------+           |              |                       |
       +----------------+             | iov_len(1024) | ----+     | 16(byte)     |                       | <= malloc()で獲得する。
       | msg_controllen |             +---------------+     |     |              |                       |
       +----------------+                                   |     +--------------+-----------------------+
       | msg_flags      |                                   |     |<------------ 1024(byte) ------------>|
       +----------------+                                   |                      A
                                                            |                      |
                                                            +----------------------+

(*1) sockaddr_nl構造体のサイズを設定する。
(*2) iovec構造体の個数(1個)を設定する。

TCPソケットオプションの使い方(TCP_CORK)

1 TCP_CORKオプションとは?

カーネル送信バッファにMSS(Maximum Segment Size)まで送信データを貯めてから、データを送信する機能です。MSSとは、MTU(Maximum Transmission Unit)からTCP/IPヘッダサイズを引いた値になります。MTUは、イーサネット環境では1500(byte)です。また、TCP/IPヘッダサイズは各々20(byte)です。さらにTCPには、TCPタイムスタンプオプション(12byte)が付くのが一般的です。したがって、MSSは1448(byte)になります。本環境でもMSSは1448(byte)です。

2 検証環境

2.1 ネットワーク構成

VMware Workstation 15 Player上の仮想マシンを使っています。 サーバとクライアントの2台構成です。図中のens33はNICの名前です。

                          192.168.2.0/24
client(ens33) -------------------------------------(ens33) server
        .105                                       .100

2.2 版数

サーバ、クライアントともに下記版数です。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

2.3 MTU

MTUを確認します。
ipコマンドの実行結果よりens33のMTUは1500(byte)であることがわかります。

[root@server ~]# ip l show dev ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:66:4c:a4 brd ff:ff:ff:ff:ff:ff

3 テストプログラム(TP)作成

3.1 テストプログラム

サーバで動作するTPを作成します。なお、エラー処理は見やすくするため意図的に省略しています。

[root@server ~]# cat sv.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
    int lfd, cfd;
    socklen_t len;
    struct sockaddr_in sv, cl;
    char buf[32];
    ssize_t n;

    lfd = socket(AF_INET, SOCK_STREAM, 0);

    sv.sin_family = AF_INET;
    sv.sin_port = htons(11111);
    sv.sin_addr.s_addr = INADDR_ANY;

    bind(lfd, (struct sockaddr *)&sv, sizeof(sv));
    listen(lfd, 5);
    memset(buf, 0, sizeof(buf));
    len = sizeof(cl);
    cfd = accept(lfd, (struct sockaddr *)&cl, &len);

    while (1) {
        n = read(cfd, buf, sizeof(buf));
        if(n > 0)
            fprintf(stderr,"%zd, %s\n", n, buf);
        else if(n == 0) {  //EOF
            close(cfd);
            return 0;
        }
        else {
            perror("read");
            return 1;
        }
    }
    close(lfd);
    return 0;
}

クライアントで動作するTPを作成します。なお、エラー処理は見やすくするため意図的に省略しています。

[root@client ~]# cat cl.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>

int main(int argc, char *argv[])
{
    struct sockaddr_in server;
    int sock, i, val;
    ssize_t n;
    char buf[10]="0123456789";

    sock = socket(AF_INET, SOCK_STREAM, 0);

    server.sin_family = AF_INET;
    server.sin_port = htons(11111);
    server.sin_addr.s_addr = inet_addr("192.168.2.100");

    val = atoi(argv[1]);
    setsockopt(sock, IPPROTO_TCP, TCP_CORK, &val, sizeof(val));
    connect(sock, (struct sockaddr *)&server, sizeof(server));

    for(i=0; i<1000; i++) {
        n = write(sock, buf, sizeof(buf));
        if(n == -1) {
            perror("write");
            return 1;
        }
    }
    close(sock);
    return 0;
}

3.2 コンパイル

サーバのTPをコンパイルします。

[root@server ~]# gcc -Wall -o sv sv.c

クライアントのTPをコンパイルします。

[root@client ~]# gcc -Wall -o cl cl.c

4 ポート番号の開放

サーバでポート番号を開放します。

[root@server ~]# firewall-cmd --add-port=11111/tcp

開放したポート番号を確認します。11111番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
11111/tcp

5 TCP_CORKオプションが有効の場合

TCP_CORKが有効だと、送信バッファにMSSまでのデータを貯めてから送信します。tcpdumpを使って、そのことを確認してみます。

サーバでTPを実行します。

[root@server ~]# ./sv

lsofコマンドを実行します。svプロセスが11111番ポートでListenしていることがわかります。
なお、lsofコマンドの使い方は、lsofコマンドの使い方 - hana_shin’s diaryを参照してください。

[root@server ~]# lsof -c sv -a -i4 -a -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      2453 root    3u  IPv4  35002      0t0  TCP *:11111 (LISTEN)

サーバでtcpdumpコマンドを実行します。なお、tcpdumpコマンドの使い方は、tcpdumpの使い方(基本編) - hana_shin’s diaryを参照してください。

[root@server ~]# tcpdump -i ens33 port 11111 -nn

クライアントでTPを実行します。TPの引数には、TCP_CORK有効(=1)を指定します。

[root@client ~]# ./cl 1

tcpdumpコマンドの実行結果を確認します。lenの部分に★が付いた6つのパケットのlenは全て1448(byte)になっています。また、●が付いた1つのパケットはlenが1312(byte)になっています。lenの合計を計算すると、1448×6 + 1312 =10000となります。TPの送信バイト数と一致していることがわかります。

[root@server ~]# tcpdump -i ens33 port 11111 -nn
-snip-
10:44:09.311769 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [S], seq 1386691091, win 29200, options [mss 1460,sackOK,TS val 1306121 ecr 0,nop,wscale 7], length 0
10:44:09.311844 IP 192.168.2.100.11111 > 192.168.2.105.53204: Flags [S.], seq 975134360, ack 1386691092, win 28960, options [mss 1460,sackOK,TS val 1146026 ecr 1306121,nop,wscale 7], length 0
10:44:09.312674 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 1306124 ecr 1146026], length 0
10:44:09.313059 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [.], seq 1:1449, ack 1, win 229, options [nop,nop,TS val 1306124 ecr 1146026], length 1448 ★
10:44:09.313124 IP 192.168.2.100.11111 > 192.168.2.105.53204: Flags [.], ack 1449, win 249, options [nop,nop,TS val 1146027 ecr 1306124], length 0
10:44:09.313495 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [.], seq 1449:2897, ack 1, win 229, options [nop,nop,TS val 1306124 ecr 1146026], length 1448 ★
10:44:09.313814 IP 192.168.2.100.11111 > 192.168.2.105.53204: Flags [.], ack 2897, win 272, options [nop,nop,TS val 1146027 ecr 1306124], length 0
10:44:09.314021 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [.], seq 2897:4345, ack 1, win 229, options [nop,nop,TS val 1306125 ecr 1146027], length 1448 ★
10:44:09.314046 IP 192.168.2.100.11111 > 192.168.2.105.53204: Flags [.], ack 4345, win 295, options [nop,nop,TS val 1146028 ecr 1306125], length 0
10:44:09.314240 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [.], seq 4345:5793, ack 1, win 229, options [nop,nop,TS val 1306126 ecr 1146027], length 1448 ★
10:44:09.314284 IP 192.168.2.100.11111 > 192.168.2.105.53204: Flags [.], ack 5793, win 317, options [nop,nop,TS val 1146028 ecr 1306126], length 0
10:44:09.314545 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [P.], seq 5793:7241, ack 1, win 229, options [nop,nop,TS val 1306126 ecr 1146028], length 1448 ★
10:44:09.314583 IP 192.168.2.100.11111 > 192.168.2.105.53204: Flags [.], ack 7241, win 340, options [nop,nop,TS val 1146028 ecr 1306126], length 0
10:44:09.315009 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [.], seq 7241:8689, ack 1, win 229, options [nop,nop,TS val 1306126 ecr 1146028], length 1448 ★
10:44:09.315195 IP 192.168.2.100.11111 > 192.168.2.105.53204: Flags [.], ack 8689, win 362, options [nop,nop,TS val 1146029 ecr 1306126], length 0
10:44:09.315569 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [FP.], seq 8689:10001, ack 1, win 229, options [nop,nop,TS val 1306127 ecr 1146028], length 1312 ●
10:44:09.319247 IP 192.168.2.100.11111 > 192.168.2.105.53204: Flags [F.], seq 1, ack 10002, win 385, options [nop,nop,TS val 1146033 ecr 1306127], length 0
10:44:09.320480 IP 192.168.2.105.53204 > 192.168.2.100.11111: Flags [.], ack 2, win 229, options [nop,nop,TS val 1306131 ecr 1146033], length 0

6 TCP_CORKオプションが無効の場合

サーバでTPを実行します。

[root@server ~]# ./sv

サーバでtcpdumpコマンドを実行します。

[root@server ~]# tcpdump -i ens33 port 11111 -nn

クライアントでTPを実行します。TPの引数には、TCP_CORK無効(=0)を指定します。

[root@client ~]#./cl 0

lenの部分を確認すると、1448(byte)のものもありますが、1448以外の長さのものが大部分であることがわかります。つまり、送信バッファにMSSサイズ分のデータが貯まる前に、データを送信していることがわかります。

[root@server ~]# tcpdump -i ens33 port 11111 -nn
-snip-
10:50:24.844911 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [S], seq 1982540112, win 29200, options [mss 1460,sackOK,TS val 1681655 ecr 0,nop,wscale 7], length 0
10:50:24.844959 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [S.], seq 3204141593, ack 1982540113, win 28960, options [mss 1460,sackOK,TS val 1521559 ecr 1681655,nop,wscale 7], length 0
10:50:24.845601 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521559], length 0
10:50:24.845682 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [P.], seq 1:11, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521559], length 10
10:50:24.845694 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 11, win 227, options [nop,nop,TS val 1521560 ecr 1681657], length 0
10:50:24.846079 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [.], seq 11:1459, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521559], length 1448
10:50:24.846113 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [P.], seq 1459:1461, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521560], length 2
10:50:24.846160 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 1459, win 249, options [nop,nop,TS val 1521560 ecr 1681657], length 0
10:50:24.846240 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 1461, win 249, options [nop,nop,TS val 1521560 ecr 1681657], length 0
10:50:24.846478 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [.], seq 1461:2909, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521560], length 1448
10:50:24.846495 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 2909, win 272, options [nop,nop,TS val 1521560 ecr 1681657], length 0
10:50:24.846553 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [P.], seq 2909:2951, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521560], length 42
10:50:24.846563 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 2951, win 272, options [nop,nop,TS val 1521560 ecr 1681657], length 0
10:50:24.846944 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [P.], seq 2951:3351, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521560], length 400
10:50:24.847789 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 3351, win 295, options [nop,nop,TS val 1521562 ecr 1681657], length 0
10:50:24.847845 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [.], seq 3351:6247, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521560], length 2896
10:50:24.847858 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 6247, win 340, options [nop,nop,TS val 1521562 ecr 1681657], length 0
10:50:24.847936 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [.], seq 6247:7695, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521560], length 1448
10:50:24.847950 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 7695, win 362, options [nop,nop,TS val 1521562 ecr 1681657], length 0
10:50:24.848017 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [P.], seq 7695:7701, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521562], length 6
10:50:24.848030 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 7701, win 362, options [nop,nop,TS val 1521562 ecr 1681657], length 0
10:50:24.848382 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [P.], seq 7701:8181, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521562], length 480
10:50:24.848427 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 8181, win 385, options [nop,nop,TS val 1521562 ecr 1681657], length 0
10:50:24.848523 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [.], seq 8181:9629, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521562], length 1448
10:50:24.848537 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 9629, win 408, options [nop,nop,TS val 1521562 ecr 1681657], length 0
10:50:24.848720 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [P.], seq 9629:9631, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521562], length 2
10:50:24.848733 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [.], ack 9631, win 408, options [nop,nop,TS val 1521563 ecr 1681657], length 0
10:50:24.848804 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [FP.], seq 9631:10001, ack 1, win 229, options [nop,nop,TS val 1681657 ecr 1521562], length 370
10:50:24.850268 IP 192.168.2.100.11111 > 192.168.2.105.53206: Flags [F.], seq 1, ack 10002, win 430, options [nop,nop,TS val 1521564 ecr 1681657], length 0
10:50:24.850991 IP 192.168.2.105.53206 > 192.168.2.100.11111: Flags [.], ack 2, win 229, options [nop,nop,TS val 1681657 ecr 1521564], length 0

7まとめ

実験結果より次のことがわかりました。
TCP_CORK有効の場合:MSSまで送信データを溜めてから送信する。
TCP_CORK無効の場合:MSSまで送信データが溜まらなくても送信する。

TCPソケットオプションの使い方(SO_LINGER)

1 SO_LINGERとは?

1.1 概要

SO_LINGERは、closeシステムコールから復帰するタイミングを制御するソケットオプションです。このオプションを使うと、FINに対するACKを受信したあとcloseシステムコールから復帰できるようになります。

具体例を以下に説明します。
SO_LINGERを使わない場合、closeシステムコールを実行すると、すぐにcloseシステムコールから復帰します。

      client                                              server
        |                                                   |
        |                                                   |
close() |----------------------- FIN ---------------------->|
復帰 <--|                                                   |
        |                                                   |
        |<---------------------- FIN + ACK -----------------|
        |                                                   |
        |                                                   |
        |----------------------- ACK ---------------------->|
        |                                                   |

一方、SO_LINGERを使うと、FINに対するACKを受信してから、closeシステムコールから復帰します。つまり、SO_LINGERを使うと、クライアントが送信したFINがサーバに届いたことがわかります。

      client                                              server
        |                                                   |
        |                                                   |
close() |----------------------- FIN ---------------------->|
        |                                                   |
        |                                                   |
復帰<-- |<---------------------- FIN + ACK -----------------|
        |                                                   |
        |                                                   |
        |----------------------- ACK ---------------------->|
        |                                                   |

1.2 補足

closeシステムコールを実行すると、FINパケットが送信されます。通常、クライアント、サーバでそれぞれcloseシステムコールを実行してTCPコネクションを開放します。サーバがFINを受信してcloseシステムコールを実行するまでの時間が長いと、以下のようにACKとFINが別々のTCPパケットでクライントに送信されます。

      client                                              server
        |                                                   |
        |                                                   |
close() |----------------------- FIN ---------------------->|
        |                                                   |
        |<---------------------- ACK -----------------------|
        |                                                   |
        |<---------------------- FIN -----------------------| close()
        |                                                   |
        |----------------------- ACK ---------------------->|
        |                                                   |

しかし、サーバがFINを受信してcloseシステムコールを実行するまでの時間が短い場合、以下のようにACKとFINが1つのTCPパケットにまとめられてクライアントに送信されます。

      client                                              server
        |                                                   |
        |                                                   |
close() |----------------------- FIN ---------------------->|
        |                                                   |
        |<---------------------- FIN + ACK -----------------| close()
        |                                                   |
        |----------------------- ACK ---------------------->|
        |                                                   |

2 検証環境

2.1 ネットワーク構成

VMware Workstation 15 Player上の仮想マシンを使っています。 サーバとクライアントの2台構成です。図中のens33はNICの名前です。

                          192.168.2.0/24
client(ens33) -------------------------------------(ens33) server
        .105                                       .100

2.2 版数

サーバ、クライアントともに下記版数です。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

3 テストプログラムの作成

テストプログラムを作成して、SO_LINGERの使用有無によって、closeの復帰タイミングがどのように変わるかを確認してみます。

3.1 サーバ側

サーバのプログラムを作成します。

[root@server ~]# vi cl.c
[root@server ~]# cat sv.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
    int lfd, cfd;
    socklen_t len;
    struct sockaddr_in sv, cl;
    char buf[32];
    ssize_t n;

    lfd = socket(AF_INET, SOCK_STREAM, 0);

    sv.sin_family = AF_INET;
    sv.sin_port = htons(11111);
    sv.sin_addr.s_addr = INADDR_ANY;

    bind(lfd, (struct sockaddr *)&sv, sizeof(sv));
    listen(lfd, 5);
    memset(buf, 0, sizeof(buf));
    len = sizeof(cl);
    cfd = accept(lfd, (struct sockaddr *)&cl, &len);

    while (1) {
        n = read(cfd, buf, sizeof(buf));
        if(n > 0)
            fprintf(stderr,"%zd, %s\n", n, buf);
        else if(n == 0) {  //EOF
            close(cfd);
            return 0;
        }
        else {
            perror("read");
            return 1;
        }
    }
    close(lfd);
    return 0;
}

3.2 クライアント側

クライアントのプログラムを作成します。

setsockoptシステムコールを使ってソケットにSO_LINGERを設定します。以下の例では、SO_LINGERのタイムアウト時間を5秒に設定しています。タイムアウトは、FINに対するACKがかえってこなかった場合、closeシステムコールから復帰する時間です。また、sleep(10)は、スリープしている間にiptablesコマンドを実行するための時間です。

[root@client ~]# vi cl.c
[root@client ~]# cat cl.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    int sock;
    struct sockaddr_in server;
    struct linger lin;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    server.sin_family = AF_INET;
    server.sin_port = htons(11111);
    server.sin_addr.s_addr = inet_addr("192.168.2.100");

    if(argc != 2) {
        printf("Usage: ./cl on|off\n");
        exit(1);
    }
    if(!strncmp("on", argv[1], 2)) {
      printf("SO_LINGER is enabled\n");
      lin.l_onoff = 1;
      lin.l_linger = 5;
    }
    else if(!strncmp("off", argv[1], 3)) {
      printf("SO_LINGER is disabled\n");
      lin.l_onoff = 0;
    }
    else {
        printf("Usage: ./cl on|off\n");
        exit(1);
    }

    setsockopt(sock, SOL_SOCKET, SO_LINGER, &lin, sizeof(lin));
    connect(sock, (struct sockaddr *)&server, sizeof(server));

    write(sock, "123", sizeof("123"));
    printf("I'm going to sleep for 10 seconds\n");
    sleep(10);
    close(sock);
    return 0;
}

3.3 コンパイル

サーバのテストプログラムをコンパイルします。

[root@server ~]# gcc -Wall -o sv sv.c
[root@server ~]# ls -l sv*
-rwxr-xr-x. 1 root root 8904  1月  9 09:17 sv
-rw-r--r--. 1 root root  860  1月  9 09:17 sv.c

クライアントのテストプログラムをコンパイルします。

[root@client ~]# gcc -Wall -o cl cl.c
[root@client ~]# ls -l cl*
-rwxr-xr-x. 1 root root    8768  1月  9 09:19 cl
-rw-r--r--. 1 root root     642  1月  9 09:15 cl.c

4 ポート番号の開放

サーバのテストプログラムは、TCPの11111番ポートでListenするので、TCPの11111番ポートを開放します。

[root@server ~]# firewall-cmd --add-port=11111/tcp
success

ポートの状態を確認します。TCPの11111番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
11111/tcp

5 SO_LINGERを使わない場合

サーバのテストプログラムを実行します。

[root@server ~]# ./sv

tcpdumpコマンドを実行します。"-ttt "は採取したパケットとパケットの時刻差分を表示するオプションです。tcpdumpコマンドの使い方は、tcpdumpの使い方(基本編) - hana_shin’s diaryを参照してください。

[root@client ~]# tcpdump -ttt -i ens33 port 11111 -nn

10秒経過するとcloseシステムコールが実行されます。このとき、closeシステムコールの実行時間が371マイクロ秒(★)であることがわかります。straceコマンドの使い方は、straceコマンドの使い方 - hana_shin’s diaryを参照してください。

[root@client ~]# strace -T -e trace=close ./cl off
close(3)                                = 0 <0.000067>
close(3)                                = 0 <0.000036>
SO_LINGER is disabled
I'm going to sleep for 10 seconds
close(3)                                = 0 <0.000371> ★
+++ exited with 0 +++

一方、FINに対するACK(2つ目のパケット)は、3379マイクロ秒後に受信していることがわかります。

[root@client ~]# tcpdump -ttt -i ens33 port 11111 -nn
-snip-
 00:00:10.012256 IP 192.168.2.105.42180 > 192.168.2.100.11111: Flags [F.], seq 5, ack 1, win 229, options [nop,nop,TS val 6796667 ecr 6908639], length 0
 00:00:00.003379 IP 192.168.2.100.11111 > 192.168.2.105.42180: Flags [F.], seq 1, ack 6, win 227, options [nop,nop,TS val 6918652 ecr 6796667], length 0
 00:00:00.000114 IP 192.168.2.105.42180 > 192.168.2.100.11111: Flags [.], ack 2, win 229, options [nop,nop,TS val 6796669 ecr 6918652], length 0

上記結果をまとめると、以下のようになります。closeシステムコールは、FINに対するACKを待たずに、復帰していることがわかります。

      client                                              server
        |                                                   |
        |                                                   |
close() |----------------------- FIN ---------------------->|
        |   *                *                              |
        |   |                |                              |
        |  371 micorseconds  |                              |
        |   |                |                              |
復帰 <--|   *              3379 micorseconds                |
        |                    |                              |
        |                    |                              |
        |                    |                              |
        |                    *                              |
        |<---------------------- FIN + ACK -----------------|
        |                    *                              |
        |                    |                              |
        |                  114 micorseconds                 |
        |                    |                              |
        |                    *                              |
        |----------------------- ACK ---------------------->|
        |                                                   |

6 SO_LINGERを使った場合

6.1 タイムアウト時間内にFINに対するACKを受信できない場合

サーバでテストプログラムを実行します。

[root@server ~]# ./sv

クライアントでテストプログラムを実行します。

[root@client ~]# strace -T -e trace=close ./cl on
close(3)                                = 0 <0.000049>
close(3)                                = 0 <0.000015>
SO_LINGER is enabled
I'm going to sleep for 10 seconds

テストプログラムがスリープしている間にiptablesを実行して、FINに対するACKを破棄する設定をします。FINに対するACKを受信できないため、SO_LINGERオプションのタイムアウトが発生します。

[root@client ~]# iptables -I INPUT -p tcp --sport 11111 -j DROP

設定を確認します。

[root@client ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp spt:11111

10秒経過するとcloseシステムコールが実行されます。このとき、closeシステムコールの実行時間が約5秒(★)であることがわかります。

[root@client ~]# strace -T -e trace=close ./cl on
close(3)                                = 0 <0.000049>
close(3)                                = 0 <0.000015>
SO_LINGER is enabled
I'm going to sleep for 10 seconds
close(3)                                = 0 <5.015066> ★
+++ exited with 0 +++

なお、iptablesの設定を削除するには、以下のように実行します。削除するルールは”-line-numbers”で確認した1番を指定します。

[root@client ~]# iptables -D INPUT 1

上記をまとめると、以下のようになります。closeシステムコールは、SO_LINGERのタイムアウトにより復帰していることがわかります。

      client                                              server
        |                                                   |
        |                                                   |
close() |----*------------------ FIN ---------------------->|
discard |<---|------------------ FIN + ACK -----------------|
        |    |                                              |
        |----|------------------ FIN(retrans) ------------->|
discard |<---|------------------ FIN + ACK -----------------|
        |    |                                              |
        |----|------------------ FIN(retrans) ------------->|
discard |<---|------------------ FIN + ACK -----------------|
        |    |                                              |
        |    |                                              |
        |  5.01 seconds                                     |
        |    |                                              |
        |    |                                              |
復帰 <--|    *                                              |
        |                                                   |

6.2 タイムアウト時間内にFINに対するACKを受信できた場合

サーバでtcコマンドを実行して、FINに対するACK送信を100ミリ秒遅延させてみます。クライアントのcloseシステムコールの実行時間が100ミリ秒になることを確認してみます。

サーバでテストプログラムを実行します。

[root@server ~]# ./sv

クライアントでテストプログラムを実行します。

[root@client ~]# strace -T -e trace=close ./cl on
close(3)                                = 0 <0.000446>
close(3)                                = 0 <0.000317>
SO_LINGER is enabled
I'm going to sleep for 10 seconds

テストプログラムがスリープしている間に、サーバでtcコマンドを実行して、FINに対するACK送信を100ミリ秒遅延させてみます。

[root@server ~]# tc qdisc add dev ens33 root netem delay 100ms

設定を確認します。

[root@server ~]# tc qdisc show dev ens33
qdisc netem 8006: root refcnt 2 limit 1000 delay 100.0ms

10秒経過するとcloseシステムコールが実行されます。このとき、closeシステムコールの実行時間が約108ミリ秒(★)であることがわかります。サーバでACK送信を遅延させた分、closeシステムコールの復帰が遅れていることがわかります。

[root@client ~]# strace -T -e trace=close ./cl on
close(3)                                = 0 <0.000446>
close(3)                                = 0 <0.000317>
SO_LINGER is enabled
I'm going to sleep for 10 seconds
close(3)                                = 0 <0.108881> ★
+++ exited with 0 +++

なお、tcコマンドの設定を削除するには、以下のように実行します。

[root@server ~]# tc qdisc del dev ens33 root

上記をまとめると、以下のようになります。closeシステムコールは、FINに対するACKを待って(約108ミリ秒)から復帰していることがわかります。

      client                                              server
        |                                                   |
        |                                                   |
close() |----------------------- FIN ---------------------->|
        |                    *                              |
        |                    |                              |
        |                    |                              |
        |                    |                              |
        |                 108 millisecond                   |
        |                    |                              |
        |                    |                              |
        |                    |                              |
        |                    *                              |
復帰 <--|<---------------------- FIN + ACK -----------------|
        |                                                   |
        |----------------------- ACK ---------------------->|
        |                                                   |