hana_shinのLinux技術ブログ

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

sadfコマンドの使い方

1 sadfコマンドとは?

sarコマンドで収集した統計情報をCSV,XML等のフォーマットに変換するコマンドです。sarコマンドの使い方は、sarコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

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

CPUは4つ搭載しています。

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

3 インストール方法

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

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

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

[root@server ~]# sadf -V
sysstat バージョン 10.1.5
(C) Sebastien Godard (sysstat <at> orange.fr)

4 オプション一覧

manより抜粋したオプション一覧を以下に示します。

OPTIONS
       -C     Tell sadf to display comments present in file.

       -d     Print  the  contents  of the data file in a format that can easily be ingested by a relational database system. The output consists of
              fields separated by a semicolon. Each record contains the hostname of the host where the file was created, the interval value  (or  -1
              if  not  applicable),  the  timestamp in a form easily acceptable by most databases, and additional semicolon separated data fields as
              specified by sar_options command line options.  Note that timestamp output can be controlled by options -T, -t and -U.

       -e [ hh:mm:ss ]
              Set the ending time of the report, given in local time. The default ending time is 18:00:00. Hours must be given in 24-hour format.

       -H     Display only the header of the report (when applicable). If no format has been specified, then the header of the  data  file  is  dis‐
              played.

       -h     When used in conjunction with option -d, all activities will be displayed horizontally on a single line.

       -j     Print the contents of the data file in JSON (JavaScript Object Notation) format. Timestamps can be controlled by options -T and -t.

       -P { cpu [,...] | ALL }
              Tell  sadf  that  processor dependent statistics are to be reported only for the specified processor or processors. Specifying the ALL
              keyword reports statistics for each individual processor, and globally for all processors. Note that processor 0 is the first  proces‐
              sor.

       -p     Print  the  contents of the data file in a format that can easily be handled by pattern processing commands like awk.  The output con‐
              sists of fields separated by a tab. Each record contains the hostname of the host where the file was created, the interval  value  (or
              -1  if  not  applicable),  the timestamp, the device name (or - if not applicable), the field name and its value.  Note that timestamp
              output can be controlled by options -T, -t and -U.

       -s [ hh:mm:ss ]
              Set the starting time of the data (given in local time), causing the sadf command to extract records time-tagged at, or following, the
              time specified. The default starting time is 08:00:00.  Hours must be given in 24-hour format.

       -T     Display timestamp in local time instead of UTC (Coordinated Universal Time).

       -t     Display timestamp in the original local time of the data file creator instead of UTC (Coordinated Universal Time).

       -U     Display timestamp (UTC - Coordinated Universal Time) in seconds from the epoch.

       -V     Print version number then exit.

       -x     Print  the  contents of the data file in XML format.  Timestamps can be controlled by options -T and -t.  The corresponding DTD (Docu‐
              ment Type Definition) and XML Schema are included in the sysstat  source  package.  They  are  also  available  at  http://pagesperso-
              orange.fr/sebastien.godard/download.html

5 事前準備

sarコマンドを実行して、テスト用のファイルを作成します。適当なタイミングでCtrl + cを押下してsarコマンドを終了します。

[root@server ~]# sar -o sar.data 2
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月20日  _x86_64_        (4 CPU)

21時15分26秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
21時15分28秒     all      0.00      0.00      0.13      0.00      0.00     99.87
21時15分30秒     all      0.00      0.00      0.12      0.00      0.00     99.88
-snip-

採取したファイルを確認します。

[root@server ~]# ls -l sar.data
-rw-r--r--. 1 root root 1553200  4月 20 21:16 sar.data

6 時刻の表示方法

6.1 時刻をUTCで表示する方法(オプションなし)

[root@server ~]# sadf sar.data
server  2       2022-04-19 12:39:36 UTC all     %user   0.00
server  2       2022-04-19 12:39:36 UTC all     %nice   0.00
server  2       2022-04-19 12:39:36 UTC all     %system 0.13
-snip-

6.2 時刻をローカル時刻で表示する方法(-T)

[root@server ~]# sadf -T sar.data
server  2       2022-04-19 21:39:36     all     %user   0.00
server  2       2022-04-19 21:39:36     all     %nice   0.00
server  2       2022-04-19 21:39:36     all     %system 0.13
-snip-

7 時刻で絞り込む方法(-s,-e)

ローカル時刻の21:39:36から21:39:38までの情報を出力してみます。-sで開始時刻、-eで終了時刻を指定します。

[root@server ~]# sadf -T -s 21:39:00 -e 21:39:38 sar.data
server  2       2022-04-19 21:39:36     all     %user   0.00
server  2       2022-04-19 21:39:36     all     %nice   0.00
server  2       2022-04-19 21:39:36     all     %system 0.13
server  2       2022-04-19 21:39:36     all     %iowait 0.00
server  2       2022-04-19 21:39:36     all     %steal  0.00
server  2       2022-04-19 21:39:36     all     %idle   99.87
server  2       2022-04-19 21:39:38     all     %user   0.00
server  2       2022-04-19 21:39:38     all     %nice   0.00
server  2       2022-04-19 21:39:38     all     %system 0.12
server  2       2022-04-19 21:39:38     all     %iowait 0.00
server  2       2022-04-19 21:39:38     all     %steal  0.00
server  2       2022-04-19 21:39:38     all     %idle   99.88
[root@server ~]#

8 CPUの統計情報を表示する方法

8.1 全CPUの統計情報を表示する方法(-P ALL)

[root@server ~]# sadf -P ALL sar.data
server	2	2022-04-19 12:39:36 UTC	all	%user	0.00
server	2	2022-04-19 12:39:36 UTC	all	%nice	0.00
server	2	2022-04-19 12:39:36 UTC	all	%system	0.13
-snip-

8.2 特定CPUの統計情報を表示する方法(-P 0,1..)

CPU0の統計情報を表示してみます。

[root@server ~]# sadf -P 0 sar.data
server  2       2022-04-19 12:39:36 UTC cpu0    %user   0.00
server  2       2022-04-19 12:39:36 UTC cpu0    %nice   0.00
server  2       2022-04-19 12:39:36 UTC cpu0    %system 0.00
-snip-

次に、CPU1,CPU3の統計情報を表示してみます。

[root@server ~]# sadf -P 1,3 sar.data
server  2       2022-04-19 12:39:36 UTC cpu1    %user   0.00
server  2       2022-04-19 12:39:36 UTC cpu1    %nice   0.00
server  2       2022-04-19 12:39:36 UTC cpu1    %system 0.50
server  2       2022-04-19 12:39:36 UTC cpu1    %iowait 0.00
server  2       2022-04-19 12:39:36 UTC cpu1    %steal  0.00
server  2       2022-04-19 12:39:36 UTC cpu1    %idle   99.50
server  2       2022-04-19 12:39:36 UTC cpu3    %user   0.00
server  2       2022-04-19 12:39:36 UTC cpu3    %nice   0.00
server  2       2022-04-19 12:39:36 UTC cpu3    %system 0.00
server  2       2022-04-19 12:39:36 UTC cpu3    %iowait 0.00
server  2       2022-04-19 12:39:36 UTC cpu3    %steal  0.00
server  2       2022-04-19 12:39:36 UTC cpu3    %idle   100.00
-snip-

9 出力ファイルのフォーマットを指定する方法

9.1 JSONフォーマットで出力する方法(-j)

[root@server ~]# sadf -T -j sar.data
{"sysstat": {
        "sysdata-version": 2.16,
        "hosts": [
                {
                        "nodename": "server",
                        "sysname": "Linux",
                        "release": "3.10.0-1160.el7.x86_64",
                        "machine": "x86_64",
                        "number-of-cpus": 4,
                        "file-date": "2022-04-19",
                        "statistics": [
                                {
                                        "timestamp": {"date": "2022-04-19", "time": "21:39:36", "utc": 0, "interval": 2},
                                        "cpu-load": [
                                                {"cpu": "all", "user": 0.00, "nice": 0.00, "system": 0.13, "iowait": 0.00, "steal": 0.00, "idle": 99.87}
                                        ]
                                },
-snip-

9.2 XMLフォーマットで出力する方法(-x)

[root@server ~]# sadf -T -x sar.data
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sysstat PUBLIC "DTD v2.16 sysstat //EN"
"http://pagesperso-orange.fr/sebastien.godard/sysstat-2.16.dtd">
<sysstat>
        <sysdata-version>2.16</sysdata-version>
        <host nodename="server">
                <sysname>Linux</sysname>
                <release>3.10.0-1160.el7.x86_64</release>
                <machine>x86_64</machine>
                <number-of-cpus>4</number-of-cpus>
                <file-date>2022-04-19</file-date>
                <statistics>
                        <timestamp date="2022-04-19" time="21:39:36" utc="0" interval="2">
                                <cpu-load>
                                        <cpu number="all" user="0.00" nice="0.00" system="0.13" iowait="0.00" steal="0.00" idle="99.87"/>
                                </cpu-load>
                        </timestamp>
-snip-

9.3 csv形式(セミコロンで区切る)で出力する方法(-d)

まず全CPUの統計情報を表示してみます。

[root@server ~]# sadf -T -d sar.data
# hostname;interval;timestamp;CPU;%user;%nice;%system;%iowait;%steal;%idle
server;2;2022-04-19 21:39:36;-1;0.00;0.00;0.13;0.00;0.00;99.87
server;2;2022-04-19 21:39:38;-1;0.00;0.00;0.12;0.00;0.00;99.88
server;2;2022-04-19 21:39:40;-1;0.12;0.00;0.12;0.00;0.00;99.75
-snip-

次に、CPU3の統計情報を表示してみます。

[root@server ~]# sadf -T -P 3 -d sar.data
# hostname;interval;timestamp;CPU;%user;%nice;%system;%iowait;%steal;%idle
server;2;2022-04-19 21:39:36;3;0.00;0.00;0.00;0.00;0.00;100.00
server;2;2022-04-19 21:39:38;3;0.00;0.00;0.00;0.00;0.00;100.00
server;2;2022-04-19 21:39:40;3;0.00;0.00;0.00;0.00;0.00;100.00
-snip-

10 sarコマンドのオプションを使う方法(-- sarのオプション)

sarコマンドのオプションを使うには、--に続けて、sarコマンドのオプションを指定します。このオプションを使うことで、CPU以外の統計情報を表示することができるようになります。

10.1 メモリ使用量の統計情報を表示する方法(-- -r)

[root@server ~]# sadf -T -- -r sar.data
server  2       2022-04-19 21:39:36     -       kbmemfree       3168184
server  2       2022-04-19 21:39:36     -       kbmemused       693108
server  2       2022-04-19 21:39:36     -       %memused        17.95
-snip-

10.2 メモリページの統計情報を表示する方法(-- -R)

[root@server ~]# sadf -T -- -R sar.data
server  2       2022-04-19 21:39:36     -       frmpg/s -34.00
server  2       2022-04-19 21:39:36     -       bufpg/s 0.00
server  2       2022-04-19 21:39:36     -       campg/s 3.00
-snip-

10.3 ブロックデバイスの統計情報を表示する方法(-- -d)

[root@server ~]# sadf -T -- -d sar.data
server	2	2022-04-19 21:39:36	dev8-0	tps	0.00
server	2	2022-04-19 21:39:36	dev8-0	rd_sec/s	0.00
server	2	2022-04-19 21:39:36	dev8-0	wr_sec/s	0.00
server	2	2022-04-19 21:39:36	dev8-0	avgrq-sz	0.00
server	2	2022-04-19 21:39:36	dev8-0	avgqu-sz	0.00
-snip-

10.4 パケット送受信の統計情報を表示する方法(-- -n DEV)

[root@server ~]# sadf -T -- -n DEV sar.data
server	2	2022-04-19 21:39:36	lo	rxpck/s	0.00
server	2	2022-04-19 21:39:36	lo	txpck/s	0.00
server	2	2022-04-19 21:39:36	lo	rxkB/s	0.00
-snip-

10.5 TCPの統計情報を表示する方法(-- -n TCP)

[root@server ~]# sadf -T -- -n TCP sar.data
server	2	2022-04-19 21:39:36	-	active/s	0.00
server	2	2022-04-19 21:39:36	-	passive/s	0.00
server	2	2022-04-19 21:39:36	-	iseg/s	0.00
-snip-

10.6 IPの統計情報を表示する方法(-- -n IP)

[root@server ~]# sadf -T -- -n IP sar.data
server	2	2022-04-19 21:39:36	-	irec/s	0.00
server	2	2022-04-19 21:39:36	-	fwddgm/s	0.00
server	2	2022-04-19 21:39:36	-	idel/s	0.00
-snip-

11 統計情報を視覚化する方法

sadfコマンドの-gオプションを使うと、sarコマンドで採取した統計情報をSVG(Scalable Vector Graphics)形式に変換することができます。SVG形式に変換することで統計情報を視覚化することができます。なお、CentOS7.9のsadfコマンドには-gオプションがないため、CentOS8.3のsadfコマンドを使ってみました。

[root@server ~]# sadf -V
sysstat バージョン 11.7.3
(C) Sebastien Godard (sysstat <at> orange.fr)

11.1 事前準備

以下の条件でstressコマンドを実行します。
・CPU0:ユーザモードのCPU使用率(%user)が100%になるような負荷を与える
・CPU1:メモリの獲得、解放を繰り返す負荷を与える

sarコマンドを実行して、統計情報を採取します。

[root@server ~]# sar -o sar.data 1

stressコマンドを実行します。なお、tasksetコマンドの使い方は、tasksetコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# taskset -c 0 stress -c 1 -q &
[1] 37519

プロセスの状態を確認します。CPU0のCPU使用率が101%であることがわかります。

[root@server ~]# ps -C stress -o comm,pid,ppid,psr,%cpu,args
COMMAND             PID    PPID PSR %CPU COMMAND
stress            37519    2079   0  0.0 stress -c 1 -q
stress            37520   37519   0  101 stress -c 1 -q

stressコマンドを実行します。

[root@server ~]# taskset -c 1 stress -m 1 -q &
[2] 37522

プロセスの状態を確認します。CPU1でメモリの獲得、解放の負荷を与えるプロセスが実行されていることがわかります。

[root@server ~]# ps -C stress -o comm,pid,ppid,psr,%cpu,args
COMMAND             PID    PPID PSR %CPU COMMAND
stress            37519    2079   0  0.0 stress -c 1 -q
stress            37520   37519   0 97.2 stress -c 1 -q
stress            37522    2079   1  0.0 stress -m 1 -q
stress            37523   37522   1  100 stress -m 1 -q

適当な時間が経過したら、stressコマンドを終了します。

[root@server ~]# pkill stress
[1]-  Terminated              taskset -c 0 stress -c 1 -q
[2]+  Terminated              taskset -c 1 stress -m 1 -q

11.2 実行結果の確認

Ctrl+Cを押下してsarコマンドを終了します。そして、採取した統計情報をSVG形式に変換します。そして、SVG形式に変換したファイルをWebブラウザにドロップします。

[root@server ~]# sadf -g -- -P ALL sar.data > cpu.svg

CPU0のユーザモードでのCPU使用率(%user)が100%程度になっていることがわかります。そして、搭載CPUが4個のため、システム全体のCPU使用率は25%程度であることがわかります。

次にCPU1の状態を確認します。CPU1ではメモリの獲得、解放を繰り返す負荷を与えているので、ユーザモードのCPU使用率(%user)、カーネルモードのCPU使用率(%system)が増加していることがわかります。

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

sarコマンドの使い方

1 sarコマンドとは?

CPU使用率、メモリ/ページング、割り込み、デバイス負荷、ネットワークの使用率など、システムの統計情報の収集および表示するコマンドです。ここでは、sarコマンドを使って採取した統計情報の見方を説明します。

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

CPUは4つ搭載しています。

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

3 インストール方法

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

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

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

[root@server ~]# sar -V
sysstat バージョン 10.1.5
(C) Sebastien Godard (sysstat <at> orange.fr)

OS起動時、sysstatサービスが自動で起動するようにします。本記事では、sysstatサービスの起動は不要ですが、統計情報を毎日自動で採取する場合はsysstatサービスを起動してください。

[root@server ~]# systemctl enable sysstat

sysstatサービスを起動します。

[root@server ~]# systemctl start sysstat

4 事前準備

本記事では、sysstatサービスではなく、手動で統計情報を採取します。統計情報を採取するときのsarコマンドの書式は以下になります。

sar -o <ファイル名> <採取間隔(秒)> <採取回数>

ここでは、sarコマンドを2秒間隔で10回実行してみます。

[root@server ~]# sar -o sar.data 2 10
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時43分54秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
21時43分56秒     all      0.00      0.00      0.13      0.00      0.00     99.87
21時43分58秒     all      0.00      0.00      0.13      0.00      0.00     99.87
21時44分00秒     all      0.00      0.00      0.25      0.00      0.00     99.75
21時44分02秒     all      0.00      0.00      0.00      0.00      0.00    100.00
21時44分04秒     all      0.00      0.00      0.13      0.00      0.00     99.87
21時44分06秒     all      0.00      0.00      0.12      0.00      0.00     99.88
21時44分08秒     all      0.12      0.00      0.12      0.00      0.00     99.75
21時44分10秒     all      0.00      0.00      0.13      0.00      0.00     99.87
21時44分12秒     all      0.00      0.00      0.00      0.00      0.00    100.00
21時44分14秒     all      0.00      0.00      0.13      0.00      0.00     99.87
平均値:      all      0.01      0.00      0.11      0.00      0.00     99.87

採取した統計情報を確認します。

[root@server ~]# ls -l sar.data
-rw-r--r--. 1 root root 362416  4月 19 21:44 sar.data

5 CPU

4章で採取した統計情報の中身を確認してみます。

5.1 全てのCPUの統計情報を参照する方法(-P ALL)

-P ALLは全てのCPUに関する統計情報を表示するオプションです。

[root@server ~]# sar -P ALL -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時39分34秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
21時39分36秒     all      0.00      0.00      0.13      0.00      0.00     99.87
21時39分36秒       0      0.00      0.00      0.00      0.00      0.00    100.00
21時39分36秒       1      0.00      0.00      0.50      0.00      0.00     99.50
21時39分36秒       2      0.00      0.00      0.50      0.00      0.00     99.50
21時39分36秒       3      0.00      0.00      0.00      0.00      0.00    100.00

21時39分36秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
21時39分38秒     all      0.00      0.00      0.12      0.00      0.00     99.88
21時39分38秒       0      0.00      0.00      0.50      0.00      0.00     99.50
21時39分38秒       1      0.00      0.00      0.00      0.00      0.00    100.00
21時39分38秒       2      0.00      0.00      0.00      0.00      0.00    100.00
21時39分38秒       3      0.00      0.00      0.00      0.00      0.00    100.00
-snip-

5.2特定CPUの統計情報を参照する方法(-P )

-Pは指定したCPUに関する統計情報を表示するオプションです。ここでは、CPU0の統計情報を参照してみます。

[root@server ~]#  sar -P 0 -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時39分34秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
21時39分36秒       0      0.00      0.00      0.00      0.00      0.00    100.00
21時39分38秒       0      0.00      0.00      0.50      0.00      0.00     99.50
21時39分40秒       0      0.00      0.00      0.00      0.00      0.00    100.00
-snip-

6 メモリ

6.1 メモリ使用量(-r)

[root@server ~]# sar -r -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時39分34秒 kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
21時39分36秒   3168184    693108     17.95      2076    344328    290680      7.28    257712    164100        24
21時39分38秒   3168192    693100     17.95      2076    344348    290680      7.28    257728    164124        44
21時39分40秒   3168192    693100     17.95      2076    344368    290680      7.28    257736    164136         0
-snip-

6.2 メモリページ(-R)

[root@server ~]# sar -R -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時39分34秒   frmpg/s   bufpg/s   campg/s
21時39分36秒    -34.00      0.00      3.00
21時39分38秒      1.00      0.00      2.49
21時39分40秒      0.00      0.00      2.50
-snip-

7 ディスク

-dはディスクI/Oの統計情報を表示するオプションです。

[root@server ~]# sar -d -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時39分34秒       DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
21時39分36秒    dev8-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分36秒   dev8-16      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分36秒   dev11-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分38秒    dev8-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分38秒   dev8-16      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分38秒   dev11-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分40秒    dev8-0      0.50      0.00     64.00    128.00      0.00      0.00      0.00      0.00
21時39分40秒   dev8-16      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分40秒   dev11-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
-snip-

8 ネットワーク

ネットワークの統計情報を参照するときのsarコマンドの書式は以下になります。

sar -n <keyword> -f <ファイル名>

keywordには、以下のものがあります。
DEV, EDEV, NFS, NFSD, SOCK, IP, EIP, ICMP, EICMP, TCP, ETCP, UDP, SOCK6, IP6, EIP6, ICMP6, EICMP6 and UDP6

8.1 NICの負荷を表示する方法(DEV)

DEVはNICのzyouhを表示するキーワードです。

[root@server ~]# sar -n DEV -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時39分34秒     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
21時39分36秒        lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分36秒     ens33      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分38秒        lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分38秒     ens33      1.00      1.00      0.06      0.16      0.00      0.00      0.00
21時39分40秒        lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分40秒     ens33      0.50      0.50      0.03      0.09      0.00      0.00      0.00
21時39分42秒        lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分42秒     ens33      0.50      0.50      0.03      0.09      0.00      0.00      0.00
21時39分43秒        lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分43秒     ens33      3.95      5.26      0.27      0.59      0.00      0.00      0.00
-snip-

8.2 IPパケットに関する情報を表示する方法(IP)

「IP」はIPパケットに関する情報を表示するキーワードです。

[root@server ~]# sar -n IP -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時39分34秒    irec/s  fwddgm/s    idel/s     orq/s   asmrq/s   asmok/s  fragok/s fragcrt/s
21時39分36秒      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
21時39分38秒      0.50      0.00      0.50      0.50      0.00      0.00      0.00      0.00
21時39分40秒      0.50      0.00      0.50      0.50      0.00      0.00      0.00      0.00
-snip-

sarが出力する項目の意味は次のとおりです。1秒間あたりの情報になります。

項目 意味
irec 受信したIPパケット数を表す。エラーで廃棄したパケット数も含む
fwddgm 宛先IPアドレスが自分自身ではないため、ルーティングしたIPパケット数を表す
idel IPパケットの到着を待っているアプリに届けられたパケット数を表す
org 送信したIPパケット数を表す
asmrq 受信したIPフラグメントのパケット数を表す
asmok 元のIPパケットに再構成したIPパケット数を表す
fragok フラグメントが必要なIPパケット数を表す
fragcrt フラグメント処理の結果、フラグメント化されたIPパケット数を表す

8.3 TCPパケットに関する情報を表示する方法(TCP)

TCP」はTCPパケットに関する情報を表示するキーワードです。

[root@server ~]# sar -n TCP -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時39分34秒  active/s passive/s    iseg/s    oseg/s
21時39分36秒      0.00      0.00      0.00      0.00
21時39分38秒      0.00      0.00      0.50      0.50
21時39分40秒      0.00      0.00      0.50      0.50
-snip-

sarが出力する項目の意味は次のとおりです。1秒間あたりの情報になります。

項目 意味
active アクティブオープンを試みた回数を表す。SYNパケットを送信してTCPコネクション確立に失敗した場合でもカウントされる
passive パッシブオープンで確立したTCPコネクション数を表す。様子がactiveのときと異なるように思う
iseg 受信したTCPパケット数を表す。プロトコル処理においてエラーと判定したTCPパケット数も含む
oseg 送信したTCPパケット数を表す。ただし、再送パケット数は除外する

9 表示時刻による絞り込み(-s,-e)

21:44:00から21:44:10までの10秒間のソケットの統計情報を参照してみます。

[root@server ~]# sar -n SOCK -s 21:44:00 -e 21:44:10 -f sar.data
Linux 3.10.0-1160.el7.x86_64 (server)   2022年04月19日  _x86_64_        (4 CPU)

21時44分00秒    totsck    tcpsck    udpsck    rawsck   ip-frag    tcp-tw
21時44分02秒       572         4         1         0         0         0
21時44分04秒       572         4         1         0         0         0
21時44分06秒       572         4         1         0         0         0
21時44分08秒       572         4         1         0         0         0
21時44分10秒       572         4         1         0         0         0
平均値:        572         4         1         0         0         0
[root@server ~]#

10 各種実験

実際に各種負荷を発生させて、sarコマンドでどのように見えるのか確認してみます。

10.1 CPU使用率(ユーザモード)の確認

sarコマンドの実行間隔が短いとCPU使用率が実際のCPU使用率より大きく表示されてしまうので、ここでは5秒間隔で実行しました。

[root@server ~]# sar -P ALL 5

次に、stress-ngコマンドを使って、CPU0のCPU使用率を10%にしてみます。以下のパラメータを指定すると、stress-ngプロセスがrand()関数を繰り返し実行します。したがって、CPU使用率はユーザモードの使用率(%user )が増えることになります。なお、stress-ngコマンドの使い方は、stress-ngコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# stress-ng -k -c 1 -l 10 --taskset 0 -q &
[1] 1730

psコマンドを実行します。stress-ngの子プロセス(PID1731)がCPU使用率10%程度であることがわかります。また、CPU0で動作していることもわかります。

[root@server ~]# ps -C stress-ng -o comm,pid,ppid,psr,%cpu
COMMAND            PID   PPID PSR %CPU
stress-ng         1730   1524   0  0.0
stress-ng         1731   1730   0 10.9

sarコマンドの実行結果を確認します。CPU0のCPU使用率が7.98%であることがわかります。sarコマンドの実行結果を継続して確認すると、CPU使用率が10%前後で推移していくことがわかります。

[root@server ~]# sar -P ALL 5
-snip-
10時18分29秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
10時18分34秒     all      2.00      0.00      0.15      0.00      0.00     97.85
10時18分34秒       0      7.98      0.00      0.20      0.00      0.00     91.82
10時18分34秒       1      0.00      0.00      0.00      0.00      0.00    100.00
10時18分34秒       2      0.20      0.00      0.20      0.00      0.00     99.60
10時18分34秒       3      0.00      0.00      0.00      0.00      0.00    100.00

stress-ngコマンドを終了します。

[root@server ~]# pkill stress-ng
[root@server ~]#
[1]+  終了                  stress-ng -k -c 1 -l 10 --taskset 0 -q

10.2 CPU使用率(カーネルモード)の確認

メモリの獲得/開放を繰り返す負荷を与えてみます。ここでは、stress-ngコマンドではなくstressコマンドを使ってみます。メモリの獲得/開放を繰り返すので、ユーザモードのCPU使用率(%user)だけでなく、カーネルモードのCPU使用率(%system)も上昇します。なお、stressコマンドは、stressコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# stress -m 1 -q &

psコマンドを実行します。stressの子プロセス(PID2062)がCPU2でメモリの獲得/開放を繰り返しています。親プロセスは子プロセスの終了をwaitシステムコールで待っています。do_wait関数はwaitシステムコールの延長で呼び出すカーネル関数です。

[root@server ~]# ps -C stress -o command,pid,ppid,rss,psr,wchan
COMMAND                        PID   PPID   RSS PSR WCHAN
stress -m 1 -q                2061   1524   428   0 do_wait
stress -m 1 -q                2062   2061 217216  2 -

sarコマンドの実行結果を確認します。CPU2のユーザモードのCPU使用率が6.60%、カーネルモードのCPU使用率が93.4%であることがわかります。

[root@server ~]# sar -P ALL 5
-snip-
10時51分29秒     CPU     %user     %nice   %system   %iowait    %steal     %idle
10時51分34秒     all      1.65      0.00     23.51      0.00      0.00     74.84
10時51分34秒       0      0.00      0.00      0.20      0.00      0.00     99.80
10時51分34秒       1      0.00      0.00      0.00      0.00      0.00    100.00
10時51分34秒       2      6.60      0.00     93.40      0.00      0.00      0.00
10時51分34秒       3      0.00      0.00      0.00      0.00      0.00    100.00

11 Excelによる統計情報の可視化

メモリの獲得/開放を繰り返したときの統計情報をExcelで可視化してみます。

11.1 統計情報の作成

[root@server ~]# sar -o sar.data 2

メモリの獲得/開放の負荷をかけるプロセスを4個起動します。

[root@server ~]# stress -m 1 -q &
[1] 11947
[root@server ~]# stress -m 1 -q &
[2] 11952
[root@server ~]# stress -m 1 -q &
[3] 11969
[root@server ~]# stress -m 1 -q &
[4] 11973

採取した統計情報をテキストに変換します。

[root@server ~]# sar -r -f sar.data > mem.txt

mem.txtをエディタ(vi等)で編集します。以下は編集前のmem.txt

先頭2行を削除。その後1行目の左端をTIMEに修正(他の単語でもOK)

最後の1行(赤枠で囲まれた部分)も削除する。

11.2 Excelによる可視化

Excelで「ファイル」->「開く」より、mem.txtを開きます。

折れ線グラフを選択します。

メモリ負荷をかけたタイミングが4回あることがわかります。青丸のところ。

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

iotopコマンドの使い方

1 iotopコマンドとは?

iotopはプロセス毎のディスクI/Oの帯域を表示するコマンドです。sar,vmstatはシステム全体のディスクI/Oの状況は確認できますが、プロセス毎の確認はできません。iotopを使うことで、ディスクI/O負荷の高いプロセスを特定することができます。

なお、vmstatコマンドの使い方は、vmstatコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。sarコマンドの使い方は、近日記事作成予定です。

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 iotopコマンドのインストール方法

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

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

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

[root@server ~]# iotop --version
iotop 0.6

4 オプション一覧

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

[root@server ~]# iotop --help
Usage: /usr/sbin/iotop [OPTIONS]

DISK READ and DISK WRITE are the block I/O bandwidth used during the sampling
period. SWAPIN and IO are the percentages of time the thread spent respectively
while swapping in and waiting on I/O more generally. PRIO is the I/O priority at
which the thread is running (set using the ionice command).

Controls: left and right arrows to change the sorting column, r to invert the
sorting order, o to toggle the --only option, p to toggle the --processes
option, a to toggle the --accumulated option, i to change I/O priority, q to
quit, any other key to force a refresh.

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -o, --only            only show processes or threads actually doing I/O
  -b, --batch           non-interactive mode
  -n NUM, --iter=NUM    number of iterations before ending [infinite]
  -d SEC, --delay=SEC   delay between iterations [1 second]
  -p PID, --pid=PID     processes/threads to monitor [all]
  -u USER, --user=USER  users to monitor [all]
  -P, --processes       only show processes, not all threads
  -a, --accumulated     show accumulated I/O instead of bandwidth
  -k, --kilobytes       use kilobytes instead of a human friendly unit
  -t, --time            add a timestamp on each line (implies --batch)
  -q, --quiet           suppress some lines of header (implies --batch)

5 Non-Interactiveモード(-b)

-bはバッチモードとも呼ぶようです。5章では、全て-bオプションを指定してiotopコマンドを実行してみます。

5.1 時刻を表示する方法(-t)

-tはiotopの実行時刻を表示するオプションです。左端(TIME列)がiotopの実行時刻です。

[root@server ~]# iotop -b -t
20:38:32 Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
20:38:32 Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
    TIME   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
20:38:32      1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 22
20:38:32      2 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kthreadd]
20:38:32      4 be/0 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kworker/0:0H]
-snip-

5.2 iotopコマンドの実行間隔を変更する方法(-d)

-dはiotopの実行間隔を指定するオプションです。デフォルトは1秒です。ここでは3秒間隔で実行してみます。iotopの実行結果を確認すると、左端(TIME列)の1回目が20:58:53、2回目が20:58:56に実行されていることがわかります。

[root@server ~]# iotop -b -t -d 3
20:58:53 Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
20:58:53 Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
    TIME   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
20:58:53      1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 22
20:58:53      2 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kthreadd]
20:58:53   1517 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % -bash
20:58:53   1011 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % rsyslogd -n [in:imjournal]
-snip-
20:58:56 Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
20:58:56 Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
    TIME   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
20:58:56     60 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.01 % [kworker/0:1]
20:58:56      1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 22
20:58:56      2 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % [kthreadd]

5.3 I/Oを実行しているプロセスだけを表示する方法(-o)

-oはI/Oを実行しているプロセスだけを表示するオプションです。これを確認するため、stressコマンドを実行して、1バイトずつ128バイトを繰り返しファイルに書き込むプロセスを3つ生成します。なお、stressコマンドのインストール方法、使い方は、stressコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# stress -d 3 --hdd-bytes 128M -q &
[1] 1905

DISK WRITE列の値を確認すると、stressプロセスがディスクに書き込みをしていることがわかります。

[root@server ~]# iotop -b -o
Total DISK READ :       0.00 B/s | Total DISK WRITE :       2.90 G/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:      35.39 M/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
  1906 be/4 root        0.00 B/s   37.78 M/s  0.00 %  0.00 % stress -d 3 --hdd-bytes 128M -q
  1907 be/4 root        0.00 B/s  108.32 M/s  0.00 %  0.00 % stress -d 3 --hdd-bytes 128M -q
  1908 be/4 root        0.00 B/s   49.13 M/s  0.00 %  0.00 % stress -d 3 --hdd-bytes 128M -q
-snip-

5.4 指定したプロセスだけを表示する方法(-p )

-pはPIDで指定したプロセスのI/Oの状況を表示するオプションです。stressコマンドを実行して、ファイルへの書き込みを行うプロセスを3つ生成します。

[root@server ~]# stress -d 3 --hdd-bytes 128M -q &
[1] 1578

psコマンドを実行します。PID1578のプロセスが親プロセス、その他が子プロセスとなります。親プロセスはwaitシステムコールを実行して、子プロセスの終了をまっています。なお、do_wait関数は、waitシステムコールの延長で呼び出すカーネル関数です。

[root@server ~]# ps -C stress -o comm,pid,ppid,wchan
COMMAND            PID   PPID WCHAN
stress            1578   1559 do_wait
stress            1579   1578 -
stress            1580   1578 -
stress            1581   1578 -

ここでは、PID1581のプロセスのI/Oの状況を表示してみます。

[root@server ~]# iotop -b -p 1581
Total DISK READ :       0.00 B/s | Total DISK WRITE :    1188.48 M/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
  1581 be/4 root        0.00 B/s 1188.48 M/s  ?unavailable?  stress -d 3 --hdd-bytes 128M -q
Total DISK READ :       0.00 B/s | Total DISK WRITE :    1064.10 M/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
  1581 be/4 root        0.00 B/s   42.34 M/s  ?unavailable?  stress -d 3 --hdd-bytes 128M -q
-snip-

5.5 指定したユーザのI/Oだけを表示する方法(-u )

私の環境ではhttpdプロセスが動作していたので、httpdプロセスを実行しているユーザ名を使ってI/Oの状況を確認してみました。

[root@server ~]# ps -C httpd -o comm,user,pid,ppid
COMMAND         USER        PID   PPID
httpd           root       1608      1
httpd           apache     1609   1608
httpd           apache     1610   1608
httpd           apache     1611   1608
httpd           apache     1612   1608
httpd           apache     1613   1608

httpdプロセスを実行しているユーザ名を指定してiotopコマンドを実行します。ユーザ名がapacheのプロセスのI/Oの状況が確認できます。確認した時点では、ディスクへのI/Oはありませんでした。

[root@server ~]# iotop -b -u apache
Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
  1609 be/4 apache      0.00 B/s    0.00 B/s  ?unavailable?  httpd -DFOREGROUND
  1610 be/4 apache      0.00 B/s    0.00 B/s  ?unavailable?  httpd -DFOREGROUND
  1611 be/4 apache      0.00 B/s    0.00 B/s  ?unavailable?  httpd -DFOREGROUND
  1612 be/4 apache      0.00 B/s    0.00 B/s  ?unavailable?  httpd -DFOREGROUND
  1613 be/4 apache      0.00 B/s    0.00 B/s  ?unavailable?  httpd -DFOREGROUND
-snip-

5.6 iotopの実行回数を指定する方法(-n)

-nはiotopの実行回数を指定するオプションです。systemdのI/Oの状況を2回表示したら、iotopコマンドを終了するようにしてみます。まず、systemdのPIDを確認します。

[root@server ~]# ps -C systemd
   PID TTY          TIME CMD
     1 ?        00:00:03 systemd

systemdのI/Oの状況を2回したあと、iotopコマンドが終了していることがわかります。

[root@server ~]# iotop -b -p 1 -n 2
Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
     1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 22
Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
     1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 22
[root@server ~]#

5.7 バッチモードで収集したログをファイルに保存する方法

systemdのI/Oの状況を5秒間隔でファイルに保存してみます。適当な時間経過したら、Ctrl+C押下してiotopコマンドを終了します。

[root@server ~]# iotop -b -p 1 -t -d 5 > iotop.txt

ファイルに保存したiotopコマンドの実行結果を確認してみます。

[root@server ~]# cat iotop.txt
19:43:46 Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
19:43:46 Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
    TIME   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
19:43:46      1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 22
19:43:51 Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
19:43:51 Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
    TIME   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
19:43:51      1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 22
19:43:56 Total DISK READ :       0.00 B/s | Total DISK WRITE :       0.00 B/s
19:43:56 Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
    TIME   TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN      IO    COMMAND
19:43:56      1 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.00 % systemd --switched-root --system --deserialize 22
[root@server ~]#

6 Interactiveモード

[root@server ~]# stress -d 3 --hdd-bytes 128M -q &
[1] 1741

psコマンドを実行して、stressプロセスのPIDを確認します。

[root@server ~]# ps -C stress -o comm,pid,ppid,wchan
COMMAND            PID   PPID WCHAN
stress            1741   1559 do_wait
stress            1742   1741 -
stress            1743   1741 -
stress            1744   1741 -

ここでは、子プロセス(PID=1742,1743,1744)の状況だけを確認してみます。

[root@server ~]# iotop -p 1742 -p 1743 -p 1744

iotopコマンドを実行すると、以下の画面が表示されます。

6.1 トグルキー

トグルキーを押下することで、iotopコマンド実行結果の表示内容を切り替えることができます。

トグルキー Non-Interactiveモードのオプション名 意味
r なし 表示順序を逆にする。左右の矢印キーを押して、表示順序を逆にしたい項目を選択する(選択中の項目は黄色)。項目を選択したらrを押下する。もう一度rを押すと元に戻ります
o -o, --only I/Oを実行しているプロセス/スレッドのみ表示する
p -P, --processes プロセスのみ表示する。スレッドは表示しない
a -a, --accumulated 帯域ではなく累積値を表示する
q なし iotopコマンドを終了する
i なし プロセス/スレッドの優先度変更

iotopコマンドを実行します。

次にoを押下すると、I/Oを実行しているプロセスだけが表示されるます。

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

sshポートフォワーディングの使い方

1 ssh ポートフォワーディングとは?

暗号化機能を備えていないアプリケーションの通信をsshが間に入って中継することで、暗号化を行うことができます。

具体的には以下のようになります。
1. アプリXがポートX宛てにデータを送信する
2. ポートXでListen(受信データ待ち)しているsshにデータが届けられる
3. sshからsshdにデータを送信する。このとき、クライアントでデータを暗号化する。
4. サーバは受信したデータを復号する
5. 復号したデータをsshdが受信する
6. sshdがポートYでListenしているアプリYにデータを転送する

   client(ローカル)                               server(リモート)
+----------------------+                      +---------------------+
|                      |                      |                     |
|                      |                      |                     |
|                      |                      |        アプリY      |
|                      |                      |          A          |
|                      |                      |          |          |
|アプリX ---> ポート(X)|                      |       ポート(Y)     |
|              |       |                      |          A          |
|              V       |                      |          |          |
|              ssh ----|----(暗号化データ)----|--------> sshd       |
|                      |                      |                     |
|                      |                      |                     |
+----------------------+                      +---------------------+

2 検証環境

2.1 ネットワーク構成

サーバとクライアントの2台構成です。図中のeth0はNICの名前です。

                           192.168.122.0/24
client(eth0) ------------------------------------------(eth0) server
        .181                                           .216

2.2 版数

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

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

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

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

sshの版数は以下のとおりです。

[root@server ~]# ssh -V
OpenSSH_7.4p1, OpenSSL 1.0.2k-fips  26 Jan 2017

3 ポートフォワーディングの書式

ポートフォワーディングは、以下の方法があります。書式は次のとおりです。

  • ローカルからリモートへのポートフォワーディング(-L)
ssh -L 転送元ポート番号:転送先ホスト:転送元ポート番号 sshサーバ
  • リモートからローカルへのポートフォワーディング(-R)
ssh -R 転送元ポート番号:転送先ホスト:転送元ポート番号 sshサーバ

4 ローカルからリモートへのポートフォワーディング

ここでは、ローカルからリモートへのポートフォワーディングを試してみます。

4.1 接続手順

サーバでncコマンドを実行します。ncプロセスが11111番ポートでListenするようにします。なお、ncコマンドのインストール方法、使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# nc -kl 11111

サーバでターミナルをもう1つ開きます。そして、lsofコマンドを実行します。ncプロセスがTCPの11111番ポートでListenしていることがわかります。なお、lsofコマンドのインストール方法、使い方はlsofコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# lsof -c nc -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      1810 root    3u  IPv6  33827      0t0  TCP *:11111 (LISTEN)
nc      1810 root    4u  IPv4  33828      0t0  TCP *:11111 (LISTEN)

クライアントの22222番ポートへのデータ送信をサーバの11111番ポートに転送する設定をします。

[root@client ~]# ssh -L 22222:192.168.122.216:11111 192.168.122.216
root@192.168.122.216's password:
Permission denied, please try again.
root@192.168.122.216's password:
Last failed login: Wed Apr 13 19:20:07 JST 2022 from client on ssh:notty
There was 1 failed login attempt since the last successful login.
Last login: Wed Apr 13 19:15:29 2022 from client
[root@server ~]#

このとき、クライアントでlsofコマンドを実行します。sshTCPの22222番ポートでListenしていることがわかります。

[root@client ~]# lsof -i -a -i:22222 -Pn
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
ssh     1704 root    4u  IPv6  36501      0t0  TCP [::1]:22222 (LISTEN)
ssh     1704 root    5u  IPv4  36502      0t0  TCP 127.0.0.1:22222 (LISTEN)

クライアントでncコマンドを実行します。

[root@client ~]# nc localhost 22222
12345

このとき、クライアントでlsofコマンドを実行します。ncコマンドは、sshがListenしているTCPの22222番ポートにTCPコネクションを確立していることがわかります。

[root@client ~]# lsof -c nc -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      1739 root    3u  IPv6  36556      0t0  TCP [::1]:40726->[::1]:22222 (ESTABLISHED)

次に、クライアントで"12345"と入力します。

[root@client ~]# nc localhost 22222
12345

サーバでncコマンドの実行結果を確認します。クライアントで入力した文字列が表示されることがわかります。

[root@server ~]# nc -kl 11111
12345

4.2 ポートフォワーディング中の状態

ポートフォワーディング中は、以下のようになっています。()内はポート番号を表します。

               client                                                 server
+-----------------------------------------+    +-----------------------------------------+
|                                         |    |                                         |
|     nc                     ssh          |    |        sshd                     nc      |
| +---------+       +------------------+  |    |  +------------------+       +---------+ |
| |         |       |                  |  |    |  |                  |       |         | |
| | (40726) |       | (22222)  (xxxxx) |  |    |  | (22)     (YYYYY) |       | (11111) | |
| +---------+       +------------------+  |    |  +------------------+       +---------+ |
|   A  |                 |        |       |    |     |          |                |       |
|   |  |                 |        |       |    |     |          |                |       |
|   |  |                 |        |       |    |     |          |                |       |
|   |  +--- localhost ---+        |       |    |     | +--------+                |       |
|   |      (127.0.0.1)            |       |    |     | |+------------------------+       |
|   |                             |       |    |     | ||                                |
|   |                             |       |    |     | ||                                |
+---|-------------------------- (eth0) ---+    +-- (eth0) -------------------------------+
    |                       .181  |                  |  .216
    |                             |                  |
    V                             +------------------+
   端末                             192.168.122.0/24

4.3 送信データの確認

ポートフォワーディングのとき、送信データが暗号化されているかどうかを確認してみます。ポートフォワーディングを使用すると送信データが暗号化されますが、使用しないと暗号化されないことを確認します。

4.3.1 ポートフォワーディングを使用しない場合

サーバで firewall-cmdコマンドを実行します。ncプロセスがListenするTCPの11111番ポートを解放します。なお、 firewall-cmdコマンドの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。

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

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

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

クライアントでtcpdumpコマンドを実行します。なお、tcpdumpコマンドの使い方は、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# tcpdump -i eth0 src host 192.168.122.181 and dst port 11111 -xx -n

クライアントでncコマンドを実行します。そして、クライアントからサーバの11111番ポートにデータ("111...111")を送信します。

[root@client ~]# nc 192.168.122.216 11111
1111111111111111111111111111111111111111

tcpdumpの実行結果を確認します。クライアントで入力した文字列("111...111")が暗号化されていないことがわかります。1はASCIIコードで0x31です。

[root@client ~]# tcpdump -i eth0 src host 192.168.122.181 and dst port 11111 -xx -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:19:45.956086 IP 192.168.122.181.37590 > 192.168.122.216.vce: Flags [P.], seq 10279061:10279102, ack 1430832885, win 229, options [nop,nop,TS val 4362757 ecr 4326873], length 41
        0x0000:  5254 006f b0ca 5254 00d0 e3a7 0800 4500
        0x0010:  005d b0ed 4000 4006 12cf c0a8 7ab5 c0a8
        0x0020:  7ad8 92d6 2b67 009c d895 5548 c6f5 8018
        0x0030:  00e5 772e 0000 0101 080a 0042 9205 0042
        0x0040:  05d9 3131 3131 3131 3131 3131 3131 3131
        0x0050:  3131 3131 3131 3131 3131 3131 3131 3131
        0x0060:  3131 3131 3131 3131 3131 0a
4.3.2 ポートフォワーディングを使用した場合

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

[root@client ~]# tcpdump -i eth0 src host 192.168.122.181 and dst port 22 -xx -n

もう1つターミナルを開きます。そして、ncコマンドを実行して、localhostの22222番ポートに文字列("111...1111")を送信します。

[root@client ~]# nc localhost 22222
1111111111111111111111111111111111111111

tcpdumpの実行結果を確認します。クライアントで入力した文字列("111...111")は暗号化されていることがわかります。

[root@client ~]# tcpdump -i eth0 src host 192.168.122.181 and dst port 22 -xx -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:10:25.072515 IP 192.168.122.181.52946 > 192.168.122.216.ssh: Flags [P.], seq 777051684:777051760, ack 3185009407, win 309, options [nop,nop,TS val 3801873 ecr 3713766], length 76
        0x0000:  5254 006f b0ca 5254 00d0 e3a7 0800 4510
        0x0010:  0080 2e56 4000 4006 9533 c0a8 7ab5 c0a8
        0x0020:  7ad8 ced2 0016 2e50 de24 bdd7 62ff 8018
        0x0030:  0135 7751 0000 0101 080a 003a 0311 0038
        0x0040:  aae6 ad38 3b20 9e12 7e80 604b 86fd ecd2
        0x0050:  9b4d 9690 9737 9dc8 64c2 f008 1258 af14
        0x0060:  c43b c994 8a10 fd86 59cd 87b5 434e 7cc4
        0x0070:  4993 9295 63cb b450 a8c3 c4db 20ab b8f0
        0x0080:  5820 fbbb 05bb cc49 db3a 6e96 034b

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

カーネルパラメータの使い方(ip_local_reserved_ports編)

1 ip_local_reserved_portsとは?

ip_local_reserved_portsは、ip_local_port_rangeが示す範囲から、OSが選択できない範囲(以下の例では50000~50003)を指定するカーネルパラメータです。以下のような設定をした場合、OSが選択できるポート番号は50004,50005の2つになります。
net.ipv4.ip_local_port_range="50000 50005"
net.ipv4.ip_local_reserved_ports = "50000-50004"

 |<------------------------ ip_local_port_range ---------------------->|
 |<------------- ip_local_reserved_ports --------->|

50000                                            50003     50004     50005
 +-------------------------------------------------+----------+--------+

なお、ip_local_port_rangeについては、カーネルパラメータの使い方(ip_local_port_range編) - hana_shinのLinux技術ブログを参照してください。

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 テストプログラム

使用するテストプログラムは、カーネルパラメータの使い方(ip_local_port_range編) - hana_shinのLinux技術ブログのものを使います。テストプログラムの第1引数は、bindシステムコールで指定するポート番号になります。ポート番号の意味は次のとおりです。

指定するポート番号 テストプログラムの挙動
0 OSが任意のポート番号を割り当てます。そして、そのポート番号でテストプログラムがListenします
0以外 指定したポート番号でテストプログラムがListenします

4 実験

ip_local_port_rangeに以下の値を設定します。

[root@server ~]# sysctl -w net.ipv4.ip_local_port_range="50000 50005"
net.ipv4.ip_local_port_range = 50000 50005

ip_local_port_rangeの値を確認します。50000から50005が予約されたことがわかります。テストプログラムの第1引数に0を指定すると、テストプログラムは50000から50005の間のポート番号でListenすることになります。

[root@server ~]# sysctl -n net.ipv4.ip_local_port_range
50000   50005

次にip_local_reserved_portsの設定をします。50000~50005の範囲の中から、50000-50003を予約します。

[root@server ~]# sysctl -w net.ipv4.ip_local_reserved_ports="50000-50003"
net.ipv4.ip_local_reserved_ports = 50000-50003

ip_local_reserved_portsの値を確認します。50000から50003が予約されたことがわかります。

[root@server ~]# sysctl -n net.ipv4.ip_local_reserved_ports
50000-50003

テストプログラムを実行します。第1引数に0を指定します。テストプログラムがListenするポート番号の選択をOSに任せます。

[root@server ~]# ./sv 0 &
[1] 1592

lsofコマンドを実行します。テストプログラムが、TCPの50004番ポートでListenしていることがわかります(期待値)。なお、lsofコマンドのインストール、使い方は、lsofコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1592 root    3u  IPv4  26640      0t0  TCP *:50004 (LISTEN)

テストプログラムを実行します(2つ目)。

[root@server ~]# ./sv 0 &
[2] 1601

lsofコマンドを実行します。2つ目のテストプログラムが、TCPの50005番ポートでListenしていることがわかります(期待値)。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1592 root    3u  IPv4  26640      0t0  TCP *:50004 (LISTEN)
sv      1601 root    3u  IPv4  25626      0t0  TCP *:50005 (LISTEN)

テストプログラムを実行します(3つ目)。しかし、エラーが発生して実行することができません(期待値)。

[root@server ~]# ./sv 0 &
[3] 1604
[root@server ~]# read: Bad file descriptor

[3]+  終了 1                ./sv 0

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ

readelfコマンドの使い方

1 readelfコマンドとは?

ELF(Executable and Linkable Format)ファイルの情報を表示するコマンドです。

ELFファイルかどうかはfileコマンドで確認することができます。
カーネルモジュールに対してfileコマンドを実行してみます。ELFファイルであることがわかります。

[root@server ~]#  file -z /lib/modules/3.10.0-1160.el7.x86_64/kernel/net/ipv4/netfilter/nf_conntrack_ipv4.ko.xz
/lib/modules/3.10.0-1160.el7.x86_64/kernel/net/ipv4/netfilter/nf_conntrack_ipv4.ko.xz: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV) (XZ compressed data)

次に実行ファイルに対して、readelfコマンドを実行してみます。ELFファイルであることがわかります。

[root@server ~]# file /usr/bin/bc
/usr/bin/bc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=60e314fae84e986e67cc4b61986bdbfc108a1f86, stripped

最後に共有ライブラリに対して、readelfコマンドを実行してみます。ELFファイルであることがわかります。

[root@server ~]# file /lib64/libc-2.17.so
/lib64/libc-2.17.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked (uses shared libs), BuildID[sha1]=f9fafde281e0e0e2af45911ad0fa115b64c2cea8, for GNU/Linux 2.6.32, not stripped

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

readelfコマンドの版数は以下のとおりです。

[root@server ~]# readelf -v
GNU readelf version 2.27-44.base.el7
Copyright (C) 2016 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) any later version.
This program has absolutely no warranty.

3 オプション一覧

[root@server ~]# readelf -h
readelf: 警告: 行なうべき事はありません。
使用法: readelf <option(s)> elf-file(s)
 ELF 形式のファイルの内容に関する情報を表示します
 Options are:
  -a --all               Equivalent to: -h -l -S -s -r -d -V -A -I
  -h --file-header       Display the ELF file header
  -l --program-headers   Display the program headers
     --segments          An alias for --program-headers
  -S --section-headers   Display the sections' header
     --sections          An alias for --section-headers
  -g --section-groups    Display the section groups
  -t --section-details   Display the section details
  -e --headers           Equivalent to: -h -l -S
  -s --syms              Display the symbol table
     --symbols           An alias for --syms
  --dyn-syms             Display the dynamic symbol table
  -n --notes             Display the core notes (if present)
  -r --relocs            Display the relocations (if present)
  -u --unwind            Display the unwind info (if present)
  -d --dynamic           Display the dynamic section (if present)
  -V --version-info      Display the version sections (if present)
  -A --arch-specific     Display architecture specific information (if any)
  -c --archive-index     Display the symbol/file index in an archive
  -D --use-dynamic       Use the dynamic section info when displaying symbols
  -x --hex-dump=<number|name>
                         Dump the contents of section <number|name> as bytes
  -p --string-dump=<number|name>
                         Dump the contents of section <number|name> as strings
  -R --relocated-dump=<number|name>
                         Dump the contents of section <number|name> as relocated bytes
  -z --decompress        Decompress section before dumping it
  -w[lLiaprmfFsoRt] or
  --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
               =frames-interp,=str,=loc,=Ranges,=pubtypes,
               =gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
               =addr,=cu_index]
                         Display the contents of DWARF2 debug sections
  --dwarf-depth=N        N 以上の深さの DIE を表示しない
  --dwarf-start=N        N 以上の深さの DIE を表示する
  -I --histogram         Display histogram of bucket list lengths
  -W --wide              出力幅が 80 文字を超えることを許可する
  @<file>                オプションを <file> から読み込む
  -H --help              この情報を表示する
  -v --version           readelf のバージョン番号を表示する

4 事前準備

readelfの動作確認のため、nf_conntrack_ipv4.koモジュールを使ってみます。/lib/modules配下から、カレントディレクトリにカーネルモジュールをコピーします。

[root@server ~]# cp /lib/modules/3.10.0-1160.el7.x86_64/kernel/net/ipv4/netfilter/nf_conntrack_ipv4.ko.xz .

カーネルモジュールを解凍します。

[root@server ~]# xz -d nf_conntrack_ipv4.ko.xz

解凍したカーネルモジュールを確認します。

[root@server ~]# ls -l nf_conntrack_ipv4.ko
-rw-r--r--. 1 root root 28973  4月  9 19:38 nf_conntrack_ipv4.ko

5 ELFヘッダを表示する方法(-h)

-hはELFヘッダを表示するオプションです。-hを使ってnf_conntrack_ipv4.koのELFヘッダを表示してみます。

[root@server ~]# readelf -h nf_conntrack_ipv4.ko
ELF ヘッダ:
  マジック:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  クラス:                            ELF64
  データ:                            2 の補数、リトルエンディアン
  バージョン:                        1 (current)
  OS/ABI:                            UNIX - System V
  ABI バージョン:                    0
  型:                                REL (再配置可能ファイル)
  マシン:                            Advanced Micro Devices X86-64
  バージョン:                        0x1
  エントリポイントアドレス:          0x0
  プログラムの開始ヘッダ:            0 (バイト)
  セクションヘッダ始点:              26128 (バイト)
  フラグ:                            0x0
  このヘッダのサイズ:                64 (バイト)
  プログラムヘッダサイズ:            0 (バイト)
  プログラムヘッダ数:                0
  セクションヘッダ:                  64 (バイト)
  セクションヘッダサイズ:            37
  セクションヘッダ文字列表索引:      36

6 セクション一覧を表示する方法(-S)

-Sはセクション一覧を表示するオプションです。nf_conntrack_ipv4.koのセクション一覧を表示してみます。nf_conntrack_ipv4.koは、37個のセクションから構成されていることがわかります。

[root@server ~]# readelf -S nf_conntrack_ipv4.ko
37 個のセクションヘッダ、始点オフセット 0x6610:

セクションヘッダ:
  [番] 名前              タイプ           アドレス          オフセット
       サイズ            EntSize          フラグ Link  情報  整列
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.gnu.build-i NOTE             0000000000000000  00000040
       0000000000000024  0000000000000000   A       0     0     4
  [ 2] .text             PROGBITS         0000000000000000  00000070
       0000000000000fa2  0000000000000000  AX       0     0     16
  [ 3] .rela.text        RELA             0000000000000000  00001018
       0000000000000cc0  0000000000000018   I      33     2     8
  [ 4] .init.text        PROGBITS         0000000000000000  00001cd8
       00000000000000f8  0000000000000000  AX       0     0     1
  [ 5] .rela.init.text   RELA             0000000000000000  00001dd0
       00000000000002a0  0000000000000018   I      33     4     8
  [ 6] .exit.text        PROGBITS         0000000000000000  00002070
       0000000000000051  0000000000000000  AX       0     0     1
  [ 7] .rela.exit.text   RELA             0000000000000000  000020c8
       0000000000000108  0000000000000018   I      33     6     8
  [ 8] __ksymtab_gpl     PROGBITS         0000000000000000  000021d0
       0000000000000010  0000000000000000   A       0     0     16
  [ 9] .rela__ksymtab_gp RELA             0000000000000000  000021e0
       0000000000000030  0000000000000018   I      33     8     8
  [10] __kcrctab_gpl     PROGBITS         0000000000000000  00002210
       0000000000000008  0000000000000000   A       0     0     8
  [11] .rela__kcrctab_gp RELA             0000000000000000  00002218
       0000000000000018  0000000000000018   I      33    10     8
  [12] .rodata           PROGBITS         0000000000000000  00002240
       00000000000001d3  0000000000000000   A       0     0     32
  [13] .rodata.str1.1    PROGBITS         0000000000000000  00002413
       00000000000000c4  0000000000000001 AMS       0     0     1
  [14] .rodata.str1.8    PROGBITS         0000000000000000  000024d8
       00000000000002cd  0000000000000001 AMS       0     0     8
  [15] .smp_locks        PROGBITS         0000000000000000  000027a8
       0000000000000004  0000000000000000   A       0     0     4
  [16] .rela.smp_locks   RELA             0000000000000000  000027b0
       0000000000000018  0000000000000018   I      33    15     8
  [17] __ksymtab_strings PROGBITS         0000000000000000  000027c8
       0000000000000014  0000000000000000   A       0     0     1
  [18] .modinfo          PROGBITS         0000000000000000  000027e0
       00000000000000e9  0000000000000000   A       0     0     32
  [19] __param           PROGBITS         0000000000000000  000028d0
       0000000000000020  0000000000000000   A       0     0     8
  [20] .rela__param      RELA             0000000000000000  000028f0
       0000000000000048  0000000000000018   I      33    19     8
  [21] __mcount_loc      PROGBITS         0000000000000000  00002938
       00000000000000f0  0000000000000000   A       0     0     8
  [22] .rela__mcount_loc RELA             0000000000000000  00002a28
       00000000000002d0  0000000000000018   I      33    21     8
  [23] __versions        PROGBITS         0000000000000000  00002d00
       0000000000000d80  0000000000000000   A       0     0     32
  [24] .data             PROGBITS         0000000000000000  00003a80
       0000000000000180  0000000000000000  WA       0     0     32
  [25] .rela.data        RELA             0000000000000000  00003c00
       0000000000000150  0000000000000018   I      33    24     8
  [26] __verbose         PROGBITS         0000000000000000  00003d50
       0000000000000168  0000000000000000  WA       0     0     8
  [27] .rela__verbose    RELA             0000000000000000  00003eb8
       0000000000000360  0000000000000018   I      33    26     8
  [28] .data..read_mostl PROGBITS         0000000000000000  00004220
       00000000000003a4  0000000000000000  WA       0     0     32
  [29] .rela.data..read_ RELA             0000000000000000  000045c8
       00000000000003c0  0000000000000018   I      33    28     8
  [30] .gnu.linkonce.thi PROGBITS         0000000000000000  000049a0
       0000000000000238  0000000000000000  WA       0     0     32
  [31] .rela.gnu.linkonc RELA             0000000000000000  00004bd8
       0000000000000030  0000000000000018   I      33    30     8
  [32] .bss              NOBITS           0000000000000000  00004c08
       0000000000000000  0000000000000000  WA       0     0     1
  [33] .symtab           SYMTAB           0000000000000000  00004c08
       0000000000000e88  0000000000000018          34    95     8
  [34] .strtab           STRTAB           0000000000000000  00005a90
       00000000000009fe  0000000000000000           0     0     1
  [35] .gnu_debuglink    PROGBITS         0000000000000000  00006490
       0000000000000020  0000000000000000           0     0     4
  [36] .shstrtab         STRTAB           0000000000000000  000064b0
       000000000000015f  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)
セクション 意味
.text 実行コードを格納するセクションです
.data 初期化済データを格納するセクションです
.bss 未初期化の変数を格納するセクションです
.nodata リードオンリーのセクションで、書き込みはできません。定数か固定文字列を格納するセクションです

7 80文字以上を表示する方法(-W)

-Wは1行に表示する文字数を指定するオプションです。デフォルトは80文字までしか表示しません。 -Wを使うことで、80文字以上の文字を表示することができます。

[root@server ~]# readelf -S -W nf_conntrack_ipv4.ko
37 個のセクションヘッダ、始点オフセット 0x6610:

セクションヘッダ:
  [番] 名前              型              アドレス         Off    サイズ ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .note.gnu.build-id NOTE            0000000000000000 000040 000024 00   A  0   0  4
  [ 2] .text             PROGBITS        0000000000000000 000070 000fa2 00  AX  0   0 16
  [ 3] .rela.text        RELA            0000000000000000 001018 000cc0 18   I 33   2  8
  [ 4] .init.text        PROGBITS        0000000000000000 001cd8 0000f8 00  AX  0   0  1
  [ 5] .rela.init.text   RELA            0000000000000000 001dd0 0002a0 18   I 33   4  8
  [ 6] .exit.text        PROGBITS        0000000000000000 002070 000051 00  AX  0   0  1
  [ 7] .rela.exit.text   RELA            0000000000000000 0020c8 000108 18   I 33   6  8
  [ 8] __ksymtab_gpl     PROGBITS        0000000000000000 0021d0 000010 00   A  0   0 16
  [ 9] .rela__ksymtab_gpl RELA            0000000000000000 0021e0 000030 18   I 33   8  8
  [10] __kcrctab_gpl     PROGBITS        0000000000000000 002210 000008 00   A  0   0  8
  [11] .rela__kcrctab_gpl RELA            0000000000000000 002218 000018 18   I 33  10  8
  [12] .rodata           PROGBITS        0000000000000000 002240 0001d3 00   A  0   0 32
  [13] .rodata.str1.1    PROGBITS        0000000000000000 002413 0000c4 01 AMS  0   0  1
  [14] .rodata.str1.8    PROGBITS        0000000000000000 0024d8 0002cd 01 AMS  0   0  8
  [15] .smp_locks        PROGBITS        0000000000000000 0027a8 000004 00   A  0   0  4
  [16] .rela.smp_locks   RELA            0000000000000000 0027b0 000018 18   I 33  15  8
  [17] __ksymtab_strings PROGBITS        0000000000000000 0027c8 000014 00   A  0   0  1
  [18] .modinfo          PROGBITS        0000000000000000 0027e0 0000e9 00   A  0   0 32
  [19] __param           PROGBITS        0000000000000000 0028d0 000020 00   A  0   0  8
  [20] .rela__param      RELA            0000000000000000 0028f0 000048 18   I 33  19  8
  [21] __mcount_loc      PROGBITS        0000000000000000 002938 0000f0 00   A  0   0  8
  [22] .rela__mcount_loc RELA            0000000000000000 002a28 0002d0 18   I 33  21  8
  [23] __versions        PROGBITS        0000000000000000 002d00 000d80 00   A  0   0 32
  [24] .data             PROGBITS        0000000000000000 003a80 000180 00  WA  0   0 32
  [25] .rela.data        RELA            0000000000000000 003c00 000150 18   I 33  24  8
  [26] __verbose         PROGBITS        0000000000000000 003d50 000168 00  WA  0   0  8
  [27] .rela__verbose    RELA            0000000000000000 003eb8 000360 18   I 33  26  8
  [28] .data..read_mostly PROGBITS        0000000000000000 004220 0003a4 00  WA  0   0 32
  [29] .rela.data..read_mostly RELA            0000000000000000 0045c8 0003c0 18   I 33  28  8
  [30] .gnu.linkonce.this_module PROGBITS        0000000000000000 0049a0 000238 00  WA  0   0 32
  [31] .rela.gnu.linkonce.this_module RELA            0000000000000000 004bd8 000030 18   I 33  30  8
  [32] .bss              NOBITS          0000000000000000 004c08 000000 00  WA  0   0  1
  [33] .symtab           SYMTAB          0000000000000000 004c08 000e88 18     34  95  8
  [34] .strtab           STRTAB          0000000000000000 005a90 0009fe 00      0   0  1
  [35] .gnu_debuglink    PROGBITS        0000000000000000 006490 000020 00      0   0  4
  [36] .shstrtab         STRTAB          0000000000000000 0064b0 00015f 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

8 セクションヘッダの中身を情報を表示する方法

8.1 HEXで表示する方法(-x)

nf_conntrack_ipv4.koには全部で37個のセクションがありました。その中に、modinfoというセクションがあります。ここでは、modinfoセクションの中身を表示してみます。

[root@server ~]# readelf -S nf_conntrack_ipv4.ko|grep modinfo
  [18] .modinfo          PROGBITS         0000000000000000  000027e0

-xオプションに18を指定して、modinfoセクションの情報を表示します。

[root@server ~]# readelf -x 18 nf_conntrack_ipv4.ko

セクション '.modinfo' の 十六進数ダンプ:
  0x00000000 6c696365 6e73653d 47504c00 616c6961 license=GPL.alia
  0x00000010 733d6970 5f636f6e 6e747261 636b0061 s=ip_conntrack.a
  0x00000020 6c696173 3d6e665f 636f6e6e 74726163 lias=nf_conntrac
  0x00000030 6b2d3200 00000000 00000000 00000000 k-2.............
  0x00000040 72657470 6f6c696e 653d5900 7268656c retpoline=Y.rhel
  0x00000050 76657273 696f6e3d 372e3900 73726376 version=7.9.srcv
  0x00000060 65727369 6f6e3d31 41354537 44414634 ersion=1A5E7DAF4
  0x00000070 41413144 41363546 33353838 30320000 AA1DA65F358802..
  0x00000080 64657065 6e64733d 6e665f63 6f6e6e74 depends=nf_connt
  0x00000090 7261636b 2c6e665f 64656672 61675f69 rack,nf_defrag_i
  0x000000a0 70763400 696e7472 65653d59 00766572 pv4.intree=Y.ver
  0x000000b0 6d616769 633d332e 31302e30 2d313136 magic=3.10.0-116
  0x000000c0 302e656c 372e7838 365f3634 20534d50 0.el7.x86_64 SMP
  0x000000d0 206d6f64 5f756e6c 6f616420 6d6f6476  mod_unload modv
  0x000000e0 65727369 6f6e7320 00                ersions .

8.2 文字列で表示する方法(-p)

-pはセクションの情報を文字列で表示するオプションです。セクション番号18を指定して、modinfoセクションの情報を表示します。modinfoのセクション情報が文字列で表示されたことがわかります。

[root@server ~]# readelf -p 18 nf_conntrack_ipv4.ko

セクション '.modinfo' の文字列ダンプ:
  [     0]  license=GPL
  [     c]  alias=ip_conntrack
  [    1f]  alias=nf_conntrack-2
  [    40]  retpoline=Y
  [    4c]  rhelversion=7.9
  [    5c]  srcversion=1A5E7DAF4AA1DA65F358802
  [    80]  depends=nf_conntrack,nf_defrag_ipv4
  [    a4]  intree=Y
  [    ad]  vermagic=3.10.0-1160.el7.x86_64 SMP mod_unload modversions

-pオプションで表示した情報は、modinfoコマンドで表示したものと同じであることがわかります。

[root@server ~]# modinfo nf_conntrack_ipv4.ko
filename:       /root/nf_conntrack_ipv4.ko
license:        GPL
alias:          ip_conntrack
alias:          nf_conntrack-2
retpoline:      Y
rhelversion:    7.9
srcversion:     1A5E7DAF4AA1DA65F358802
depends:        nf_conntrack,nf_defrag_ipv4
intree:         Y
vermagic:       3.10.0-1160.el7.x86_64 SMP mod_unload modversions
signer:         CentOS Linux kernel signing key
sig_key:        E1:FD:B0:E2:A7:E8:61:A1:D1:CA:80:A2:3D:CF:0D:BA:3A:A4:AD:F5
sig_hashalgo:   sha256

9 シンボルテーブルを表示する方法(-s)

-sはシンボルテーブルを表示するオプションです。シンボルテーブルは、変数名、関数名、ファイル名を含んだセクションになります。以下の実行結果では、FUNCは関数名、OBJECTは変数名、FILE はファイル名を表しています。

[root@server ~]# readelf -s nf_conntrack_ipv4.ko

シンボルテーブル '.symtab' は 155 個のエントリから構成されています:
  番号:      値         サイズ タイプ  Bind   Vis      索引名
-snip-
    26: 0000000000000110    28 FUNC    LOCAL  DEFAULT    2 ipv4_nlattr_tuple_size
    27: 0000000000000040    80 OBJECT  LOCAL  DEFAULT   12 ipv4_nla_policy
    28: 0000000000000130   140 FUNC    LOCAL  DEFAULT    2 ipv4_tuple_to_nlattr
-snip-
    60: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS nf_conntrack_proto_icmp.c
-snip-

10 プログラムヘッダを表示する方法(-l)

-lはプログラムヘッダを表示するオプションです。ここでは、tcpdumpコマンドのプログラムヘッダを表示してみます。

[root@server ~]# readelf -l /usr/sbin/tcpdump

Elf ファイルタイプは EXEC (実行可能ファイル) です
エントリポイント 0x406f65
9 個のプログラムヘッダ、始点オフセット 64

プログラムヘッダ:
  タイプ        オフセット          仮想Addr           物理Addr
            ファイルサイズ        メモリサイズ         フラグ 整列
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x00000000000001f8 0x00000000000001f8  R E    8
  INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                 0x000000000000001c 0x000000000000001c  R      1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000dfa74 0x00000000000dfa74  R E    200000
  LOAD           0x00000000000dfde0 0x00000000006dfde0 0x00000000006dfde0
                 0x0000000000003cb8 0x0000000000162bf0  RW     200000
  DYNAMIC        0x00000000000dfdf8 0x00000000006dfdf8 0x00000000006dfdf8
                 0x0000000000000200 0x0000000000000200  RW     8
  NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                 0x0000000000000044 0x0000000000000044  R      4
  GNU_EH_FRAME   0x00000000000d2d08 0x00000000004d2d08 0x00000000004d2d08
                 0x000000000000135c 0x000000000000135c  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
  GNU_RELRO      0x00000000000dfde0 0x00000000006dfde0 0x00000000006dfde0
                 0x0000000000000220 0x0000000000000220  R      1

 セグメントマッピングへのセクション:
  セグメントセクション...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag .note.gnu.build-id
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .jcr .dynamic .got

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ


https://docs.oracle.com/cd/E19253-01/819-0391/chapter6-79797/index.html

ECONNREFUSEDとECONNRESETについて

1 ECONNREFUSEDとECONNRESETとは?

エラー種別 意味
ECONNREFUSED TCPの状態がSYN-SENT状態のとき、RSTパケットを受信すると発生します(TCPの場合)
ICMP port unreachableを受信すると発生します(UDPの場合)
ECONNRESET TCPの状態がSYN-SENT以外のとき、RSTパケットを受信すると発生します。たとえば、TCPコネクション確立後、相手からRSTパケットを受信すると発生します

2 検証環境

2.1 ネットワーク構成

サーバとクライアントの2台構成です。図中のeth0はNICの名前です。

                           192.168.122.0/24
client(eth0) ------------------------------------------(eth0) server
        .181                                           .216

2.2 版数

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

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

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

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

3 検証

ECONNREFUSEDとECONNRESETがどのような時に発生するか、検証環境で確認してみます。

3.1 ECONNREFUSED(TCPの場合)

クライアントが、Listenしていないポート番号(TCP)に対してSYNパケットを送信すると、サーバがRSTパケットを送信します。このとき、クライアントでECONNREFUSEDが発生します。

             client(192.168.122.181)                   server(192.168.122.216)
                  |                                          |
                  |                                          |
                  |                                          |
  nc server 11111 |--------------- SYN --------------------->|
                  |                                          |
                  |                                          |
     ECONNREFUSED |<-------------- RST ----------------------|
                  |                                          |
                  |                                          |

テストで使用するTCPの11111番ポートを開放します。なお、firewall-cmdの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。

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

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

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

サーバでtcpdumpを実行します。このとき、TCPの11111番ポートのみをキャプチャします。なお、tcpdumpの使い方は、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログを参照してください。

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

クライアントでncコマンドを実行します。このとき、サーバのIPアドレス、ポート番号(11111)を指定します。ncコマンドを実行すると、"Connection refused."が表示されることがわかります。なお、ncコマンドの使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# nc 192.168.122.216 11111
Ncat: Connection refused.

tcpdumpの実行結果を確認すると、サーバからクライアントにRSTパケットが送信されていることがわかります。

[root@server ~]# tcpdump -i eth0 port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
19:49:04.276073 IP 192.168.122.181.45640 > 192.168.122.216.11111: Flags [S], seq 2464899466, win 29200, options [mss 1460,sackOK,TS val 2328460 ecr 0,nop,wscale 7], length 0
19:49:04.276148 IP 192.168.122.216.11111 > 192.168.122.181.45640: Flags [R.], seq 0, ack 2464899467, win 0, length 0

3.2 ECONNREFUSED(UDPの場合)

待ち受けしていないUDPポートにパケットを送信すると、サーバからICMPのICMP port unreachableが送信されます。

             client(192.168.122.181)                   server(192.168.122.216)
                  |                                          |
                  |                                          |
                  |                                          |
  nc server 11111 |--------------- UDP datagram ------------>|
                  |                                          |
                  |                                          |
     ECONNREFUSED |<-------------- ICMP port unreachable ----|
                  |                                          |
                  |                                          |

tcpdumpを実行します。このとき、UDPの11111番ポート宛てとICMPパケットのみをキャプチャします。

[root@server ~]# tcpdump -i eth0 port 11111 or icmp -nn

ncコマンドを実行します。

[root@client ~]# nc -u 192.168.122.216 11111

次に、クライアントからサーバにUDPデータグラムを送信します。12345とタイプしたあとリターンキーを押下します。リターンを押下すると、"Connection refused."が表示されることがわかります。

[root@client ~]# nc -u 192.168.122.216 11111
12345
Ncat: Connection refused.

サーバからクライアントにICMP port unreachableが送信されていることがわかります。

[root@server ~]# tcpdump -i eth0 udp port 11111 or icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:46:54.778677 IP 192.168.122.181.59201 > 192.168.122.216.11111: UDP, length 6
20:46:54.778757 IP 192.168.122.216 > 192.168.122.181: ICMP 192.168.122.216 udp port 11111 unreachable, length 42

3.3 ECONNRESET

ECONNRESETは、SYN-SENT以外のときに、RSTパケットを受信すると発生します。ここでは、TCPコネクションが確立したあと、サーバからクライアントにRSTパケットを送信することで、ECONNRESETを発生させてみます。

             client(192.168.122.181)                   server(192.168.122.216)
                  |                                          |
                  |                                          | nc -kl 11111
                  |                                          |
                  |                                          |
  nc server 11111 |--------------- SYN --------------------->|
                  |<-------------- SYN+ACK ------------------|
                  |--------------- ACK --------------------->| -*-
                  |                                          |  |
                  |                                          |  |
                  |                                          |  | TCPコネクション確立状態
                  |                                          |  |    (ESTABLISHED状態)
                  |                                          |  |
       ECONNRESET |<-------------- RST ----------------------| -*-
                  |                                          |

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

[root@server ~]# nc -kl 11111

クライアントでncコマンドを実行します。ncコマンドを実行すると、サーバとクライアントの間でTCPコネクションが確立されます。詳細は、TCPコネクションの確立、解放シーケンス - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# nc 192.168.122.216 11111

もう1つターミナルをオープンしてssコマンドを実行します。サーバとクライアント間でTCPコネクションが確立されたことがわかります。なお、ssコマンドの使い方は、ssコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

root@client ~]# ss -n state established 'dport == :11111'
Netid Recv-Q Send-Q                                 Local Address:Port                                                Peer Address:Port
tcp   0      0                                    192.168.122.181:41616                                            192.168.122.216:11111

サーバでCtrl+zを押下して、サーバのncプロセスを停止します。

[root@server ~]# nc -kl 11111
^Z
[1]+  停止                  nc -kl 11111

psコマンドを実行してプロセスの状態を確認します。ncプロセスが停止状態(T)であることがわかります。なお、psコマンドの使い方は、psコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# ps -C nc -o comm,pid,state
COMMAND           PID S
nc               1467 T

クライアントからサーバにデータを送信します。改行コードも含めて6バイト送信します。

[root@client ~]# nc 192.168.122.216 11111
12345

サーバのncプロセスは停止しているので、カーネルの受信バッファからデータを読み出すことができません。ssコマンドを使ってそのときの様子を確認します。Recv-Qはカーネル受信バッファに残っている受信データのバイト数を表しています。したがって、6バイトのデータがカーネル受信バッファに残ったままになっていることがわかります。詳細はssコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# ss -tn4 'sport == :11111'
State       Recv-Q Send-Q Local Address:Port                Peer Address:Port
ESTAB       6      0      192.168.122.216:11111              192.168.122.181:41616

サーバのncプロセスを終了します。

[root@server ~]# kill -9 1467
[1]+  強制終了            nc -kl 11111

カーネル受信バッファに受信データが残ったままプロセスが終了すると、データ送信元にRSTパケットが送信されます。

[root@server ~]# tcpdump -i eth0 tcp port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:21:42.428698 IP 192.168.122.216.11111 > 192.168.122.181.41616: Flags [R.], seq 2836811943, ack 622903595, win 227, options [nop,nop,TS val 2681900 ecr 1590444], length 0

ちなみに、カーネル受信バッファに受信データが残っているかどうかの判定処理は以下の場所です。カーネル受信バッファに受信データが残っている場合、tcp_send_active_reset関数の延長でRSTパケットを送信します。

2004 void tcp_close(struct sock *sk, long timeout)
-snip-
2050         } else if (data_was_unread) {
2051                 /* Unread data was tossed, zap the connection. */
2052                 NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
2053                 tcp_set_state(sk, TCP_CLOSE);
2054                 tcp_send_active_reset(sk, sk->sk_allocation);

クライアントでECONNRESETが発生していることがわかります。

[root@client ~]# nc 192.168.122.216 11111
12345
Ncat: Connection reset by peer.

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ