hana_shinのLinux技術ブログ

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

sha256sumコマンドの使い方

1 はじめに

チェックサムを計算するコマンドの使い方について説明します。

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 コマンド一覧

チェックサムを求めるコマンドとして、下記6コマンドがインストールされていました。

4 コマンドのオプション一覧

どのコマンドのオプションも同じでした。ここでは、sha256sumのオプション一覧を示します。

[root@server ~]# sha256sum --help
使用法: sha256sum [OPTION]... [FILE]...
SHA256 (256-bit) チェックサムを表示または照合します。
FILE の指定が無いか、 - が指定された場合、標準入力から読み込みます。

  -b, --binary         バイナリモードで読み込む
  -c, --check          FILE から SHA256 チェックサムを読み込み、照合する
      --tag            create a BSD-style checksum
  -t, --text           テキストモードで読み込む (デフォルト)
  Note: There is no difference between binary and text mode option on GNU system.

The following four options are useful only when verifying checksums:
      --quiet          don't print OK for each successfully verified file
      --status         don't output anything, status code shows success
      --strict         exit non-zero for improperly formatted checksum lines
  -w, --warn           warn about improperly formatted checksum lines

      --help     この使い方を表示して終了する
      --version  バージョン情報を表示して終了する

The sums are computed as described in FIPS-180-2.  When checking, the input
should be a former output of this program.  The default mode is to print
a line with checksum, a character indicating input mode ('*' for binary,
space for text), and name for each FILE.

GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
sha256sum の翻訳に関するバグは <http://translationproject.org/team/ja.html> に連絡してください。
完全な文書を参照する場合は info coreutils 'sha256sum invocation' を実行してください。

5 チェックサムを計算する方法

各コマンドを使って、チェックサムを計算してみます。

5.1 事前準備

テスト用のファイルを作成します。
なお、ファイルの作成方法は、ファイルの作り方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# head -c 1m /dev/urandom > 1m.data
[root@server ~]# ls -l 1m.data
-rw-r--r--. 1 root root 1048576  1月  5 21:00 1m.data

5.2 md5sumコマンドによるチェックサムの計算

[root@server ~]# md5sum 1m.data
1dd822615ae0d42a29769e9eb2f98bbd  1m.data

5.3 sha1sumコマンドによるチェックサムの計算

[root@server ~]# sha1sum 1m.data
d4b7495965fdb7a45dc5012f6e8d7e58701221eb  1m.data

5.4 sha256sumコマンドによるチェックサムの計算

[root@server ~]# sha256sum 1m.data
93b48c575ff85a26db49b8f249135e9f894c8481a106435acda43767e735affd  1m.data

5.5 sha512sumコマンドによるチェックサムの計算

[root@server ~]# sha512sum 1m.data
e88e86635008260c7a4d5d6ab1630ef255345fbf7216c3befbe51a1151185d0af07ea832949325a3537a50003d746524d9f2c5b1299a71afd2ec5b70ea6f63b6  1m.data

6 ファイルが壊れていないかどうかの確認方法

6.1 事前準備

テスト用のファイルを作成します。

[root@server ~]# head -c 1m /dev/urandom > 1m.data
[root@server ~]# head -c 2m /dev/urandom > 2m.data
[root@server ~]# head -c 3m /dev/urandom > 3m.data

チェックサムを格納したファイル(sha256sum.txt)を作成します。
なお、findコマンドの使い方は、findコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# find . -maxdepth 1 -type f -name "*.data" -exec sha256sum {} >> sha256sum.txt \;

作成したファイルの中身を確認します。

[root@server ~]# cat sha256sum.txt
3d4d3872471a4c2b48257a3ef6184d3c7fbe076f4675bb0b6466dee24249fb6d  ./1m.data
55abe3fd286f14c520ffe59c81ef7ecb499c0b3a5513de2b47627bd6036e8229  ./2m.data
fb9139b751d748aac424a269816209ba06c85baa83fd8b5c65ce85f548c8bdfe  ./3m.data

6.2 ファイルが壊れていないことの確認

ファイルが壊れていないことを確認します。
ファイル作成後、何も変更を加えていないので、チェックサムのチェックはいずれも「完了」となります。

[root@server ~]# sha256sum -c sha256sum.txt
./1m.data: 完了
./2m.data: 完了
./3m.data: 完了

次にファイルを意図的に壊してみます。

[root@server ~]# echo 1>> 3m.data

sha256sum.txtを使って、再度ファイルが壊れていないかどうか確認してみます。
変更を加えたファイルが「失敗」と表示されていることがわかります。

[root@server ~]# sha256sum -c sha256sum.txt
./1m.data: 完了
./2m.data: 完了
./3m.data: 失敗
sha256sum: 警告: 1 個の計算したチェックサムが一致しませんでした

7 ファイルが壊れていないかどうかの確認方法(実践編)

理研からダウンロードしたisoファイルが壊れていないかどうかを確認してみます。
http://ftp.riken.jp/Linux/centos/7.9.2009/isos/x86_64/
ISOファイルをダウンロードします。

[root@server ~]# wget http://ftp.riken.jp/Linux/centos/7.9.2009/isos/x86_64/CentOS-7-x86_64-NetInstall-2009.iso

ダウンロードしたISOファイルを確認します。

[root@server ~]# ls -l CentOS-7-x86_64-NetInstall-2009.iso
-rw-r--r--. 1 root root 602931200 10月 27  2020 CentOS-7-x86_64-NetInstall-2009.iso

次に、sha256sum.txtファイルをダウンロードします。

[root@server ~]# wget http://ftp.riken.jp/Linux/centos/7.9.2009/isos/x86_64/sha256sum.txt

sha256sum.txtファイルの中身を確認します。
4ファイルのsha256チェックサムが格納されていることがわかります。

[root@server ~]# cat sha256sum.txt
689531cce9cf484378481ae762fae362791a9be078fda10e4f6977bf8fa71350  CentOS-7-x86_64-Everything-2009.iso
b79079ad71cc3c5ceb3561fff348a1b67ee37f71f4cddfec09480d4589c191d6  CentOS-7-x86_64-NetInstall-2009.iso
07b94e6b1a0b0260b94c83d6bb76b26bf7a310dc78d7a9c7432809fb9bc6194a  CentOS-7-x86_64-Minimal-2009.iso
e33d7b1ea7a9e2f38c8f693215dd85254c3a4fe446f93f563279715b68d07987  CentOS-7-x86_64-DVD-2009.iso


CentOS-7-x86_64-NetInstall-2009.iso」が壊れていなかどうかを確認してみます。
「完了」(★印)と表示されているので、ファイルが壊れていないことがわかります。
CentOS-7-x86_64-NetInstall-2009.iso」以外のファイルはダウンロードしていないので、
「そのようなファイルやディレクトリはありません」と表示されています。

[root@server ~]# sha256sum -c sha256sum.txt
sha256sum: CentOS-7-x86_64-Everything-2009.iso: そのようなファイルやディレクトリはありません
CentOS-7-x86_64-Everything-2009.iso: オープンまたは読み込みに失敗しました
CentOS-7-x86_64-NetInstall-2009.iso: 完了 ★
sha256sum: CentOS-7-x86_64-Minimal-2009.iso: そのようなファイルやディレクトリはありません
CentOS-7-x86_64-Minimal-2009.iso: オープンまたは読み込みに失敗しました
sha256sum: CentOS-7-x86_64-DVD-2009.iso: そのようなファイルやディレクトリはありません
CentOS-7-x86_64-DVD-2009.iso: オープンまたは読み込みに失敗しました
sha256sum: 警告: 一覧にある 3 個のファイルが読み込めませんでした

Z 参考情報

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

nmapコマンドの使い方

1 nmapコマンドとは?

ポートが開放されているかどうかを確認するために使うコマンドです。

2 検証環境

2.1 ネットワーク構成

サーバとクライアントの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 インストール方法

クライアントにnmapをインストールします。

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

nmapの版数を確認します。

[root@server ~]# nmap -V

Nmap version 6.40 ( http://nmap.org )
Platform: x86_64-redhat-linux-gnu
Compiled with: nmap-liblua-5.2.2 openssl-1.0.2k libpcre-8.32 libpcap-1.5.3 nmap-libdnet-1.12 ipv6
Compiled without:
Available nsock engines: epoll poll select

4 オプション一覧

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

[root@server ~]#  nmap --help
Nmap 6.40 ( http://nmap.org )
Usage: nmap [Scan Type(s)] [Options] {target specification}
TARGET SPECIFICATION:
  Can pass hostnames, IP addresses, networks, etc.
  Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0.0-255.1-254
  -iL <inputfilename>: Input from list of hosts/networks
  -iR <num hosts>: Choose random targets
  --exclude <host1[,host2][,host3],...>: Exclude hosts/networks
  --excludefile <exclude_file>: Exclude list from file
HOST DISCOVERY:
  -sL: List Scan - simply list targets to scan
  -sn: Ping Scan - disable port scan
  -Pn: Treat all hosts as online -- skip host discovery
  -PS/PA/PU/PY[portlist]: TCP SYN/ACK, UDP or SCTP discovery to given ports
  -PE/PP/PM: ICMP echo, timestamp, and netmask request discovery probes
  -PO[protocol list]: IP Protocol Ping
  -n/-R: Never do DNS resolution/Always resolve [default: sometimes]
  --dns-servers <serv1[,serv2],...>: Specify custom DNS servers
  --system-dns: Use OS's DNS resolver
  --traceroute: Trace hop path to each host
SCAN TECHNIQUES:
  -sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans
  -sU: UDP Scan
  -sN/sF/sX: TCP Null, FIN, and Xmas scans
  --scanflags <flags>: Customize TCP scan flags
  -sI <zombie host[:probeport]>: Idle scan
  -sY/sZ: SCTP INIT/COOKIE-ECHO scans
  -sO: IP protocol scan
  -b <FTP relay host>: FTP bounce scan
PORT SPECIFICATION AND SCAN ORDER:
  -p <port ranges>: Only scan specified ports
    Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9
  -F: Fast mode - Scan fewer ports than the default scan
  -r: Scan ports consecutively - don't randomize
  --top-ports <number>: Scan <number> most common ports
  --port-ratio <ratio>: Scan ports more common than <ratio>
SERVICE/VERSION DETECTION:
  -sV: Probe open ports to determine service/version info
  --version-intensity <level>: Set from 0 (light) to 9 (try all probes)
  --version-light: Limit to most likely probes (intensity 2)
  --version-all: Try every single probe (intensity 9)
  --version-trace: Show detailed version scan activity (for debugging)
SCRIPT SCAN:
  -sC: equivalent to --script=default
  --script=<Lua scripts>: <Lua scripts> is a comma separated list of
           directories, script-files or script-categories
  --script-args=<n1=v1,[n2=v2,...]>: provide arguments to scripts
  --script-args-file=filename: provide NSE script args in a file
  --script-trace: Show all data sent and received
  --script-updatedb: Update the script database.
  --script-help=<Lua scripts>: Show help about scripts.
           <Lua scripts> is a comma separted list of script-files or
           script-categories.
OS DETECTION:
  -O: Enable OS detection
  --osscan-limit: Limit OS detection to promising targets
  --osscan-guess: Guess OS more aggressively
TIMING AND PERFORMANCE:
  Options which take <time> are in seconds, or append 'ms' (milliseconds),
  's' (seconds), 'm' (minutes), or 'h' (hours) to the value (e.g. 30m).
  -T<0-5>: Set timing template (higher is faster)
  --min-hostgroup/max-hostgroup <size>: Parallel host scan group sizes
  --min-parallelism/max-parallelism <numprobes>: Probe parallelization
  --min-rtt-timeout/max-rtt-timeout/initial-rtt-timeout <time>: Specifies
      probe round trip time.
  --max-retries <tries>: Caps number of port scan probe retransmissions.
  --host-timeout <time>: Give up on target after this long
  --scan-delay/--max-scan-delay <time>: Adjust delay between probes
  --min-rate <number>: Send packets no slower than <number> per second
  --max-rate <number>: Send packets no faster than <number> per second
FIREWALL/IDS EVASION AND SPOOFING:
  -f; --mtu <val>: fragment packets (optionally w/given MTU)
  -D <decoy1,decoy2[,ME],...>: Cloak a scan with decoys
  -S <IP_Address>: Spoof source address
  -e <iface>: Use specified interface
  -g/--source-port <portnum>: Use given port number
  --data-length <num>: Append random data to sent packets
  --ip-options <options>: Send packets with specified ip options
  --ttl <val>: Set IP time-to-live field
  --spoof-mac <mac address/prefix/vendor name>: Spoof your MAC address
  --badsum: Send packets with a bogus TCP/UDP/SCTP checksum
OUTPUT:
  -oN/-oX/-oS/-oG <file>: Output scan in normal, XML, s|<rIpt kIddi3,
     and Grepable format, respectively, to the given filename.
  -oA <basename>: Output in the three major formats at once
  -v: Increase verbosity level (use -vv or more for greater effect)
  -d: Increase debugging level (use -dd or more for greater effect)
  --reason: Display the reason a port is in a particular state
  --open: Only show open (or possibly open) ports
  --packet-trace: Show all packets sent and received
  --iflist: Print host interfaces and routes (for debugging)
  --log-errors: Log errors/warnings to the normal-format output file
  --append-output: Append to rather than clobber specified output files
  --resume <filename>: Resume an aborted scan
  --stylesheet <path/URL>: XSL stylesheet to transform XML output to HTML
  --webxml: Reference stylesheet from Nmap.Org for more portable XML
  --no-stylesheet: Prevent associating of XSL stylesheet w/XML output
MISC:
  -6: Enable IPv6 scanning
  -A: Enable OS detection, version detection, script scanning, and traceroute
  --datadir <dirname>: Specify custom Nmap data file location
  --send-eth/--send-ip: Send using raw ethernet frames or IP packets
  --privileged: Assume that the user is fully privileged
  --unprivileged: Assume the user lacks raw socket privileges
  -V: Print version number
  -h: Print this help summary page.
EXAMPLES:
  nmap -v -A scanme.nmap.org
  nmap -v -sn 192.168.0.0/16 10.0.0.0/8
  nmap -v -iR 10000 -Pn -p 80
SEE THE MAN PAGE (http://nmap.org/book/man.html) FOR MORE OPTIONS AND EXAMPLES

5 基本的な使いかた

nmapコマンドは以下のように実行します。
デフォルトは、TCPによるポートスキャンを実行します。

nmap <スキャン対象のホストのIPアドレス(またはホスト名) >

5.1 開放されているポート番号を確認する方法

"開放されている"とは、パケットフィルタ(iptables)でパケットがフィルタされていないことを意味します。まず、開放されているポートをポート番号で確認します。

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

[root@server ~]#

次に開放されているポートをサービス名で調べてみます。ssh(22)のポートだけが、開放されていることがわかります。なお、サービス名は/etc/servicesに登録されています。

[root@server ~]# firewall-cmd --list-services
ssh

次に、クライアントでnmapを実行します。このとき、スキャン対象のIPアドレス(192.168.2.100)を指定します。実行結果を確認すると、sshはopenと表示されています。sshdとやりとりが可能な状態になっています。また、"Not shown: 999 filtered ports"と表示(★)されています。これは、999個のポートがパケットフィルタでフィルタされていることを意味しています。

[root@client ~]# nmap 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-07 21:49 JST
Nmap scan report for 192.168.2.100
Host is up (0.0013s latency).
Not shown: 999 filtered ports ★
PORT   STATE SERVICE
22/tcp open  ssh
MAC Address: xx:xx:xx:yy:yy:yy (CCC)

Nmap done: 1 IP address (1 host up) scanned in 4.69 seconds

5.2 実験1

サーバで11111番ポートを開放して、nmapの実行結果がどのようになるのかを確認してみます。

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

次に、クライアントでnmapを実行します。

[root@client ~]# nmap 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-07 21:55 JST
Nmap scan report for 192.168.2.100
Host is up (0.00049s latency).
Not shown: 998 filtered ports
PORT      STATE  SERVICE
22/tcp    open   ssh
11111/tcp closed vce ★
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 8.07 seconds

11111番ポートが、filteredからclosed(★印)に変化したことがわかります。closedは、ポートスキャンのパケットが11111番ポートまで到達したけど、そのポート番号でListenしているプロセスが存在しないことを意味しています。

この時の様子を以下に図示します。11111番ポートに対するSYNパケットに対して、RSTパケットが送信されています。RSTパケットが送信されるのは、11111番ポートでListenしているプロセスがいないためです。

  client                                server(11111)
   |                                       |
   |----------------- SYN ---------------->|
   |<---------------- RST------------------| 
   |                                       |

5.3 実験2

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

[root@server ~]# nc -kl 11111

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

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

次に、クライアントでnmapを実行します。

[root@client ~]# nmap 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-07 23:09 JST
Nmap scan report for 192.168.2.100
Host is up (0.00067s latency).
Not shown: 998 filtered ports
PORT      STATE SERVICE
22/tcp    open  ssh
11111/tcp open  vce ★
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 12.30 seconds

今度は、11111番ポートがopen(★)になりました。つまり、11111番ポートでListenするプロセス(nc)が存在することを意味しています。

この時の様子を以下に図示します。SYNパケットに対して、SYN+ACKパケットを返信しています。なお、クライアントはSYN+ACKを受信すると、サーバにRSTを送信しています。

  client                                server(11111)
   |                                       |
   |----------------- SYN ---------------->|
   |<---------------- SYN +ACK ------------| 
   |----------------- RST ---------------->|
   |                                       |

6 TCPポートをスキャンする方法

開放されているポート番号を確認します。22番ポートだけが開放されていることがわかります。

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

[root@server ~]# firewall-cmd --list-services
ssh
[root@server ~]#

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

sshの22番ポートが開放されているかどうか確認してみます。openと表示されているので、開放されていることがわかります。

[root@client ~]# nmap -p22 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 15:16 JST
Nmap scan report for 192.168.2.100
Host is up (0.0010s latency).
PORT   STATE SERVICE
22/tcp open  ssh
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 0.72 seconds

次にTCPの11111番ポートが開放されているかどうか確認してみます。11111番ポートは、filteredと表示されているので、開放されていないことがわかります。

[root@client ~]# nmap -p11111 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 15:17 JST
Nmap scan report for 192.168.2.100
Host is up (0.00075s latency).
PORT      STATE    SERVICE
11111/tcp filtered vce
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 0.71 seconds

6.2 範囲を指定する方法(-p)

TCPの20番ポートから30番ポートについて、ポートが開放されているかどうかを確認してみます。sshの22番ポートだけが開放されていることがわかります。

[root@client ~]# nmap -p20-30 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 15:19 JST
Nmap scan report for 192.168.2.100
Host is up (0.00055s latency).
PORT   STATE    SERVICE
20/tcp filtered ftp-data
21/tcp filtered ftp
22/tcp open     ssh
23/tcp filtered telnet
24/tcp filtered priv-mail
25/tcp filtered smtp
26/tcp filtered rsftp
27/tcp filtered nsw-fe
28/tcp filtered unknown
29/tcp filtered msg-icp
30/tcp filtered unknown
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 1.90 seconds

7 UDPポートをスキャンする方法(-sU)

ncコマンドを実行します。UDPの11111番ポートでデータを待ち受けます。

[root@server ~]# nc -ul 11111

ncプロセスが使用しているポート番号を確認します。

[root@server ~]# lsof -i4:11111 -a -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      5399 root    4u  IPv4  74676      0t0  UDP *:11111

UDPの11111番ポートを開放します。

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

ポート番号を確認します。

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

サーバの11111番ポーに対してnmapを実行します。なぜか状態がclosedとなってしまいます。バグですかね?

[root@client ~]# nmap -sU -p11111 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 15:41 JST
Nmap scan report for 192.168.2.100
Host is up (0.00079s latency).
PORT      STATE  SERVICE
11111/udp closed unknown
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 0.74 seconds

8 ネットワーク内のホスト一覧を表示する方法

8.1 ARPを使う方法(-sn)

ARPを使って192.168.2.0/24に存在するホストを確認してみます。ARP要求に応答したホストは"Host is up."と表示されます。全部で5台のホストが存在することがわかります。

[root@client ~]# nmap -sn 192.168.2.0/24

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 18:42 JST
Nmap scan report for WRC-2533GS2 (192.168.2.1)
Host is up (-0.10s latency).
MAC Address: xx:xx:xx:xx:xx:xx (Unknown)
Nmap scan report for DESKTOP-XXXXXXX (192.168.2.12)
Host is up (0.00016s latency).
MAC Address: xx:xx:xx:xx:xx:xx (Unknown)
Nmap scan report for 192.168.2.100
Host is up (0.00036s latency).
MAC Address: xx:xx:xx:xx:xx:xx (ZZZZZZ)
Nmap scan report for client (192.168.2.37)
Host is up.
Nmap scan report for 192.168.2.105
Host is up.
Nmap done: 256 IP addresses (5 hosts up) scanned in 4.27 seconds

8.2 特定のホストをポートスキャンから除外する方法(--exclude)

192.168.2.0/24のネットワークで192.168.2.252と192.168.2.253のホストに対してポートスキャンを実行しないようにしてみます。nmapを実行すると、以下のように192.168.2.252と192.168.2.253に対してポートスキャンが実行されていないことがわかります。

[root@client ~]# nmap --exclude 192.168.2.252,192.168.2.253 -sL 192.168.2.0/24
-snip-
Nmap scan report for 192.168.2.250
Nmap scan report for 192.168.2.251
Nmap scan report for 192.168.2.254
Nmap scan report for 192.168.2.255
Nmap done: 254 IP addresses (0 hosts up) scanned in 2.51 seconds

9 パケットトレースを行う方法(--packet-trace)

packet-traceオプションを使うと、サーバとクライアントのやりとりがわかります。以下の例では、まずはじめにARPを使って、サーバのMACアドレスを得ています。次に、クライアントからサーバの11111番ポートにSYNパケットが2回送信されています。そのあと、サーバからクライアントにSYN+ACKパケットが2回返信されています。

[root@client ~]# nmap --packet-trace -p11111 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 18:14 JST
SENT (0.0609s) ARP who-has 192.168.2.100 tell 192.168.2.105
RCVD (0.0615s) ARP reply 192.168.2.100 is-at xx:xx:xx:yy:yy:yy
-snip-
SENT (0.2815s) TCP 192.168.2.105:43579 > 192.168.2.100:11111 S ttl=47 id=26942 iplen=44  seq=3321984755 win=1024 <mss 1460>
SENT (0.3824s) TCP 192.168.2.105:43580 > 192.168.2.100:11111 S ttl=57 id=56633 iplen=44  seq=3321919218 win=1024 <mss 1460>
RCVD (0.2822s) TCP 192.168.2.100:11111 > 192.168.2.105:43579 SA ttl=64 id=0 iplen=44  seq=4010614734 win=29200 <mss 1460>
RCVD (0.3829s) TCP 192.168.2.100:11111 > 192.168.2.105:43580 SA ttl=64 id=0 iplen=44  seq=1336599785 win=29200 <mss 1460>
Nmap scan report for 192.168.2.100
Host is up (0.00068s latency).
PORT      STATE SERVICE
11111/tcp open  vce
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 0.51 seconds

10 送信元ポート番号を指定する方法(-g)

gオプションを指定すると、ポートスキャンの送信元ポート番号を明に指定することができます。gオプションを指定しないと、ポートスキャンのパケットを送信する毎に送信元ポート番号が変化します。

送信元ポート番号を22222に指定してみます。このとき、さきほど確認したpacket-traceオプションを指定して、送信元ポート番号を確認してみます。送信元ポート番号が22222であることがわかります。

[root@client ~]# nmap --packet-trace -g 22222 -p11111 192.168.2.100
-snip-
SENT (0.4400s) TCP 192.168.2.105:22222 > 192.168.2.100:11111 S ttl=47 id=28127 iplen=44  seq=1253010836 win=1024 <mss 1460>
SENT (0.5493s) TCP 192.168.2.105:22222 > 192.168.2.100:11111 S ttl=41 id=63524 iplen=44  seq=1252945301 win=1024 <mss 1460>
RCVD (0.4407s) TCP 192.168.2.100:11111 > 192.168.2.105:22222 SA ttl=64 id=0 iplen=44  seq=217630012 win=29200 <mss 1460>
RCVD (0.5499s) TCP 192.168.2.100:11111 > 192.168.2.105:22222 SA ttl=64 id=0 iplen=44  seq=219335868 win=29200 <mss 1460>
Nmap scan report for 192.168.2.100
Host is up (0.0012s latency).
PORT      STATE SERVICE
11111/tcp open  vce
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 0.72 seconds
[root@client ~]#

11 サービスのバージョンを確認する方法

11.1 sshのバージョン確認

sshのバージョンを確認します。

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

sshdプロセスがListenしているポート番号を確認します。22番ポートでListenしていることがわかります。

[root@server ~]# lsof -c sshd -a -iTCP -a -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    1116 root    3u  IPv4  27789      0t0  TCP *:22 (LISTEN)
sshd    1116 root    4u  IPv6  27374      0t0  TCP *:22 (LISTEN)
-snip-

クライアントでnmapを実行します。sshのバージョンがOpenSSH 7.4 (protocol 2.0)であることがわかります。

[root@client ~]# nmap -sV -p22 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 15:57 JST
Nmap scan report for 192.168.2.100
Host is up (0.00056s latency).
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.4 (protocol 2.0)
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 0.88 seconds

11.2 httpdのバージョン確認

httpdを起動します。

[root@server ~]# systemctl start httpd

httpdの版数を確認します。

[root@server ~]# httpd -v
Server version: Apache/2.4.6 (CentOS)
Server built:   Nov 10 2021 14:26:31

httpdプロセスがListenしているポート番号を確認します。80番ポートでListenしていることがわかります。

[root@server ~]# lsof -c httpd -a -iTCP -a -P
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
httpd   5797   root    4u  IPv6  77404      0t0  TCP *:80 (LISTEN)
httpd   5798 apache    4u  IPv6  77404      0t0  TCP *:80 (LISTEN)
httpd   5799 apache    4u  IPv6  77404      0t0  TCP *:80 (LISTEN)
httpd   5800 apache    4u  IPv6  77404      0t0  TCP *:80 (LISTEN)
httpd   5801 apache    4u  IPv6  77404      0t0  TCP *:80 (LISTEN)
httpd   5802 apache    4u  IPv6  77404      0t0  TCP *:80 (LISTEN)

httpdに対してnmapを実行します。httpdの版数が"Apache httpd 2.4.6 *1"と表示されていることがわかります。

[root@client ~]# nmap -sV -p80 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 16:02 JST
Nmap scan report for 192.168.2.100
Host is up (0.00074s latency).
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.6 ((CentOS))
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.87 seconds

12 ポートスキャンの結果を確認する方法(--reason)

reasonオプションを使うと、どのような理由でポートへのアクセスが失敗したのかの理由を調べることができます。

開放されているポートを確認します。sshのポートだけが開放されていることがわかります。

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

[root@server ~]# firewall-cmd --list-services
ssh
[root@server ~]#

12.1 ポートが解放されていない場合

開放されていないTCPの11111番ポートに対してnmapを実行してみます。
ポートがパケットフィルタでフィルタされており開放されていない状態の場合、host-prohibitedという理由が表示されます。

[root@client ~]# nmap --reason -p11111 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 16:29 JST
Nmap scan report for 192.168.2.100
Host is up, received arp-response (0.00066s latency).
PORT      STATE    SERVICE REASON
11111/tcp filtered vce     host-prohibited
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 0.72 seconds

12.2 解放したポートでListenするプロセスが存在しない場合

TCPの11111番ポートを開放します。

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

開放したTCPの11111番ポートに対してnmapを実行してみます。
今度はresetが表示されました。resetはポートは開放されているけど、そのポートでListenしているプロセスが存在しないことを表しています。

[root@client ~]# nmap --reason -p11111 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 16:31 JST
Nmap scan report for 192.168.2.100
Host is up, received arp-response (0.00074s latency).
PORT      STATE  SERVICE REASON
11111/tcp closed vce     reset
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 0.73 seconds

12.3 開放したポートでListenするプロセスが存在する場合

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

[root@server ~]# nc -kl 11111

ncプロセスがListenしているポート番号を確認します。

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

TCPの11111番ポートに対してnmapを実行してみます。
今度はsyn-ackが表示されました。syn-ackは、そのポートでListenしているプロセスが存在することを表しています。

[root@client ~]# nmap --reason -p11111 192.168.2.100

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 16:33 JST
Nmap scan report for 192.168.2.100
Host is up, received arp-response (0.00056s latency).
PORT      STATE SERVICE REASON
11111/tcp open  vce     syn-ack
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 1 IP address (1 host up) scanned in 0.74 seconds

13 ファイルからターゲットを読み込んでポートスキャンをする方法(-iL)

スキャン対象のターゲットのIPアドレスを記載したテキストファイルを作成します。
8.8.8.8はGoogleのオープンDNSサーバのIPアドレスです。インターネット上のサーバに対して実行するのはあまりよくありませんが、例として一度実行してみます。

[root@client ~]# vi scan_list.txt
[root@client ~]# cat scan_list.txt
8.8.8.8 192.168.2.100

nmapを実行します。GoogleのオープンDNSサーバは、DNSのポート(53)とHTTPS(443)のポートが開放されていることがわかります。また、テスト環境のサーバは、ssh(22)のポートが開放されていることがわかります。

[root@client ~]# nmap -iL scan_list.txt

Starting Nmap 6.40 ( http://nmap.org ) at 2022-01-08 19:39 JST
Nmap scan report for dns.google (8.8.8.8)
Host is up (0.016s latency).
Not shown: 998 filtered ports
PORT    STATE SERVICE
53/tcp  open  domain
443/tcp open  https

Nmap scan report for 192.168.2.100
Host is up (0.00057s latency).
Not shown: 999 filtered ports
PORT   STATE SERVICE
22/tcp open  ssh
MAC Address: xx:xx:xx:yy:yy:yy (ZZZZZZ)

Nmap done: 2 IP addresses (2 hosts up) scanned in 25.93 seconds

Z 参考情報

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

DYNAMIC DEBUGの使い方

1 はじめに

カーネルデバッグの1つにprintk()を使う方法があります。この方法だと、カーネルをビルドする必要があり、手間がかかります。しかし、DYNAMIC DEBUGを使うと、カーネルをビルドする必要がなくデバッグメッセージを出力することができます。ただし、デバッグメッセージは、あらかじめ決められた関数、フォーマットで出力されるため、デバッグの目的にそわないことがあります。目的に応じて使い分ける必要があります。なお、DYNAMIC DEBUGを使うと、以下に示すpr_debug()のデバッグメッセージを出力できるようになります。

以下は、net/ipv4/ping.cより抜粋したものです。

     54 static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int mask)
     55 {
     56         int res = (num + net_hash_mix(net)) & mask;
     57
     58         pr_debug("hash(%d) = %d\n", num, res);
     59         return res;
     60 }

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

DYNAMIC DEBUGを使うためには、CONFIG_DYNAMIC_DEBUGが有効になっている必要があります。 私の環境は、デフォルトで有効になっていました。もし無効なら、CONFIG_DYNAMIC_DEBUGを有効にしてカーネルをビルドする必要があります。

[root@server ~]# cat /boot/config-3.10.0-1160.el7.x86_64 |grep CONFIG_DYNAMIC_DEBUG
CONFIG_DYNAMIC_DEBUG=y

3 デバッグメッセージ出力の有効/無効化方法

ここでは、デバッグメッセージ出力の有効/無効化の方法を説明します。例として、ping.cファイルのデバッグメッセージ出力の有効/無効化について確認してみます。

3.1 有効化(+p)

controlファイルの初期状態を確認します。"="の右側が"_"になっているので、デバッグメッセージの出力が無効になっていることがわかります。

[root@server ~]#  cat /sys/kernel/debug/dynamic_debug/control | grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =_ "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =_ "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =_ "ping_sendmsg(sk=%p,sk->num=%u)\012"
-snip-

デバッグメッセージの出力を有効化します。

[root@server ~]# echo 'file net/ipv4/ping.c +p' > /sys/kernel/debug/dynamic_debug/control

有効化すると、"="の右側が"p"になっているので、デバッグメッセージの出力が有効になったことがわかるます。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control|grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =p "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =p "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =p "ping_sendmsg(sk=%p,sk->num=%u)\012"
-snip-

3.2 無効化(-p)

次は、デバッグメッセージの出力を無効化してみます。

[root@server ~]# echo 'file net/ipv4/ping.c -p' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。無効化すると、"="の右側が"_"になっているので、デバッグメッセージの出力が無効になっていることがわかります

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control | grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =_ "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =_ "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =_ "ping_sendmsg(sk=%p,sk->num=%u)\012"

3.3 実行結果

ping.cで定義している関数のデバッグメッセージを出力してみます。

[root@server ~]# echo 'file net/ipv4/ping.c +p' > /sys/kernel/debug/dynamic_debug/control

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。なお、pingの使い方は、pingコマンドの使い方 - hana_shinのLinux技術ブログを参照ください。

[root@server ~]# ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認すると、ping_v4_unhash(),ping_rcv()のデバッグメッセージが出力されていることがわかります。

[root@server ~]# journalctl -f
 1月 02 20:19:13 server kernel: ping_v4_unhash(isk=ffff90f8af2b5000,isk->num=0)
 1月 02 20:19:13 server kernel: ping_rcv(skb=ffff90f8b773a300,id=0981,seq=0001)
 1月 02 20:19:13 server kernel: hash(2433) = 21
 1月 02 20:19:13 server kernel: try to find: num = 2433, daddr = 192.168.2.100, dif = 2
 1月 02 20:19:13 server kernel: no socket, dropping

最初(ping_v4_unhash..)のメッセージは、139行目で出力していることがわかります。

    134
    135 static void ping_v4_unhash(struct sock *sk)
    136 {
    137         struct inet_sock *isk = inet_sk(sk);
    138
    139         pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
    140         write_lock_bh(&ping_table.lock);
    141         if (sk_hashed(sk)) {
    142                 hlist_nulls_del(&sk->sk_nulls_node);
    143                 sk_nulls_node_init(&sk->sk_nulls_node);
    144                 sock_put(sk);
    145                 isk->inet_num = 0;
    146                 isk->inet_sport = 0;
    147                 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
    148         }
    149         write_unlock_bh(&ping_table.lock);
    150 }

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c -p' > /sys/kernel/debug/dynamic_debug/control

4 行数を表示する方法(l)

デバッグメッセージを出力する場所(何行目か?)を表示してみます。

[root@server ~]# echo 'file net/ipv4/ping.c +pl' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。"="の右側が"pl"になっているので、デバッグメッセージの出力(p)と行数(l)の出力が有効になっていることがわかります。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control |grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =pl "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =pl "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =pl "ping_sendmsg(sk=%p,sk->num=%u)\012"
-snip-

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。

[root@server ~]# ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認します。 ping_v4_unhashはping.cの139行目、ping_rcvはping.cの732行目で表示されていることがわかります。

[root@server ~]# journalctl -f
 1月 02 20:37:08 server kernel: 139: ping_v4_unhash(isk=ffff90f8b3799800,isk->num=0)
 1月 02 20:37:08 server kernel: 732: ping_rcv(skb=ffff90f9300e6300,id=0a5d,seq=0001)
 1月 02 20:37:08 server kernel: 58: hash(2653) = 49
 1月 02 20:37:08 server kernel: 161: try to find: num = 2653, daddr = 192.168.2.100, dif = 2
 1月 02 20:37:08 server kernel: 745: no socket, dropping

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c -pl' > /sys/kernel/debug/dynamic_debug/control

5 関数名を表示する方法(f)

デバッグメッセージを出力する関数名を表示してみます。

[root@server ~]# echo 'file net/ipv4/ping.c +plf' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。"="の右側が"pfl"になっているので、デバッグメッセージの出力(p)、関数名(f)、行数(l)の出力が有効になっていることがわかります。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control |grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =pfl "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =pfl "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =pfl "ping_sendmsg(sk=%p,sk->num=%u)\012"
-snip-

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。

[root@server ~]# ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認します。

[root@server ~]# journalctl -f
 1月 02 21:00:42 server kernel: ping_v4_unhash:139: ping_v4_unhash(isk=ffff90f8b66d8400,isk->num=0)
 1月 02 21:00:42 server kernel: ping_rcv:732: ping_rcv(skb=ffff90f8a0e91000,id=0b39,seq=0001)
 1月 02 21:00:42 server kernel: ping_hashfn:58: hash(2873) = 13
 1月 02 21:00:42 server kernel: ping_v4_lookup:161: try to find: num = 2873, daddr = 192.168.2.100, dif = 2
 1月 02 21:00:42 server kernel: ping_rcv:745: no socket, dropping

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c -plf' > /sys/kernel/debug/dynamic_debug/control

6 出力コンテキストを表示する方法(t)

デバッグメッセージがプロセスコンテキストで出力されたのか、割り込みコンテキストで出力されたのかを表示します。デバッグメッセージが、プロセスコンテキストで出力された場合、PIDが表示されます。割り込みコンテキストで出力された場合、intrと表示されます。

[root@server ~]# echo 'file net/ipv4/ping.c +plft' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。"="の右側が"pflt"になっているので、デバッグメッセージの出力(p)、関数名(f)、行数(l)、コンテキスト(t)の出力が有効になっていることがわかります。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control |grep "net/ipv4/ping.c"
net/ipv4/ping.c:242 [ping]ping_close =pflt "isk->refcnt = %d\012"
net/ipv4/ping.c:241 [ping]ping_close =pflt "ping_close(sk=%p,sk->num=%u)\012"
net/ipv4/ping.c:481 [ping]ping_sendmsg =pflt "ping_sendmsg(sk=%p,sk->num=%u)\012"

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。

[root@server ~]# ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認します。 1行目は、デバッグメッセージがプロセスコンテキスト(PID=3201)で出力されていることがわかります。そして、2行目以降は割り込みコンテキスト(intr)で出力されていることがわかります。さらに、具体的に言うと、2行目以降は、ソフト割り込み(net_rx_action関数の延長)で実行されています。

[root@server ~]# journalctl -f
 1月 02 21:35:44 server kernel: [3201] ping_v4_unhash:139: ping_v4_unhash(isk=ffff90f8b66db000,isk->num=0)
 1月 02 21:35:44 server kernel: <intr> ping_rcv:732: ping_rcv(skb=ffff90f8a0e90b00,id=0c81,seq=0001)
 1月 02 21:35:44 server kernel: <intr> ping_hashfn:58: hash(3201) = 21
 1月 02 21:35:44 server kernel: <intr> ping_v4_lookup:161: try to find: num = 3201, daddr = 192.168.2.100, dif = 2
 1月 02 21:35:44 server kernel: <intr> ping_rcv:745: no socket, dropping

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c -plft' > /sys/kernel/debug/dynamic_debug/control

7 特定のデバッグメッセージを表示する方法(line)

ping.cファイルの139行目のデバッグメッセージを表示してみます。このとき、関数名(f)、コンテキスト(t)も表示してみます。

[root@server ~]# echo 'file net/ipv4/ping.c line 139 +plft' > /sys/kernel/debug/dynamic_debug/control

controlファイルを確認します。"="の右側が"pflt"になっているので、デバッグメッセージの出力(p)、関数名(f)、行数(l)、コンテキスト(t)の出力が有効になっていることがわかります。

[root@server ~]# cat /sys/kernel/debug/dynamic_debug/control | grep "net/ipv4/ping.c"
-snip-
net/ipv4/ping.c:139 [ping]ping_v4_unhash =pflt "ping_v4_unhash(isk=%p,isk->num=%u)\012"
-snip-

もう1つターミナルを開いて、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

デフォルトGWに対してpingを1回実行してみます。

[root@server ~]#  ping -c 1 192.168.2.1

journalctlコマンドの実行結果を確認します。 ping.cファイル内の139行目のpr_debugメッセージが出力されていることがわかります。

[root@server ~]# journalctl -f
 1月 02 23:51:32 server kernel: [4473] ping_v4_unhash:139: ping_v4_unhash(isk=ffff90f8b379d000,isk->num=0)

確認が終わったので、controlファイルの設定内容をデフォルトに戻します。

[root@server ~]# echo 'file net/ipv4/ping.c line 139 -plft' > /sys/kernel/debug/dynamic_debug/control

8 モジュールのデバッグメッセージを表示する方法(module)

ここでは、nf_conntrackモジュール内の全てのデバッグメッセージを表示してみます。

[root@server ~]# echo 'module nf_conntrack +plft' > /sys/kernel/debug/dynamic_debug/control
[root@server ~]# journalctl -f
-snip-
 1月 02 23:56:30 server kernel: <intr> tcp_in_window:628: tcp_in_window:
 1月 02 23:56:30 server kernel: <intr> tcp_in_window:631: seq=1512910124 ack=1019883933+(0) sack=1019883933+(0) win=4052 end=1512910124
 1月 02 23:56:30 server kernel: <intr> tcp_in_window:637: tcp_in_window: sender end=1512910124 maxend=1512942252 maxwin=1051136 scale=8 receiver end=1019883933 maxend=1020921365 maxwin=32128 scale=7
 1月 02 23:56:30 server kernel: <intr> tcp_in_window:647: tcp_in_window: I=1 II=1 III=1 IV=1
-snip-

Z 参考情報

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

nstatコマンドの使い方

1 nstatコマンドとは?

ネットワークの統計情報を表示するコマンドです。 CentOS6までは、ネットワークの統計情報はnetstat -sで取得できました。 CentOS7では、netstatコマンドではなくssコマンドを使うことが推奨されています。 しかし、ssコマンドにはnetstat -sに相当する情報が取得できません。 nstatコマンドは、netstat -sに相当する情報を取得することができます。 ここでは、nstatコマンドの使い方について説明します。

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 インストール方法

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

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

[root@server ~]# nstat -V
nstat utility, iproute2-ss170501

4 オプション一覧

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

[root@server ~]# nstat -h
Usage: nstat [OPTION] [ PATTERN [ PATTERN ] ]
   -h, --help           this message
   -a, --ignore         ignore history
   -d, --scan=SECS      sample every statistics every SECS
   -j, --json           format output in JSON
   -n, --nooutput       do history only
   -p, --pretty         pretty print
   -r, --reset          reset history
   -s, --noupdate       don't update history
   -t, --interval=SECS  report average over the last SECS
   -V, --version        output version information
   -z, --zeros          show entries with zero activity

5 単純な使い方(オプション指定なし)

nstatコマンドを実行します。オプションを指定しないと、前回nstatコマンドを実行してからの差分を表します。 左から2列目の情報が統計情報のカウンタです。統計情報のカウンタが0以外のものを表示します。

[root@server ~]# nstat
#kernel
IpInReceives                    31                 0.0
IpInDelivers                    25                 0.0
IpOutRequests                   17                 0.0
-snip-

6 特定の統計情報だけを表示する方法

6. 1 統計情報を1つ指定する場合

ここでは、ICMP echo requestパケットの統計情報だけを表示してみます。 IcmpInMsgsは、ICMP Echo Requestパケット数を表す統計情報です。 実行結果より、前回nstat コマンドを実行したときから、ICMP Echo Requestパケットを受信していないことがわかります。

[root@server ~]# nstat IcmpInMsgs
#kernel

次に、サーバに対してpingを1回実行してみます。 なお、pingの使い方は、pingコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# ping -c 1 server

サーバでnstatコマンドを実行します。IcmpInMsgsが1つ増加したことがわかります。

[root@server ~]# nstat IcmpInMsgs
#kernel
IcmpInMsgs                      1                  0.0

6. 2 統計情報を複数指定する場合

次に、統計情報を2つ指定してみます

[root@server ~]# nstat IcmpOutEchoReps IcmpInMsgs
#kernel

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

[root@client ~]# ping -c 1 192.168.2.100
[root@server ~]# nstat IcmpOutEchoReps IcmpInMsgs
#kernel
IcmpInMsgs                      1                  0.0
IcmpOutEchoReps                 1                  0.0

7 カウンタ値が0の統計情報も表示する方法(-z)

[root@server ~]# nstat -z
#kernel
IpInReceives                    35                 0.0
IpInHdrErrors                   0                  0.0
IpInAddrErrors                  0                  0.0
-snip-

Z 参考情報

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

lsofコマンドの使い方

1 lsofコマンドとは?

ファイルをオープンしているプロセスを表示するコマンドです

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 インストール方法

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

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

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

[root@server ~]# lsof -v
lsof version information:
    revision: 4.87

4 オプション一覧

[root@server ~]# lsof -h
lsof 4.87
 latest revision: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/
 latest FAQ: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/FAQ
 latest man page: ftp://lsof.itap.purdue.edu/pub/tools/unix/lsof/lsof_man
 usage: [-?abhKlnNoOPRtUvVX] [+|-c c] [+|-d s] [+D D] [+|-f[gG]] [+|-e s]
 [-F [f]] [-g [s]] [-i [i]] [+|-L [l]] [+m [m]] [+|-M] [-o [o]] [-p s]
[+|-r [t]] [-s [p:s]] [-S [t]] [-T [t]] [-u s] [+|-w] [-x [fl]] [-Z [Z]] [--] [names]
Defaults in parentheses; comma-separated set (s) items; dash-separated ranges.
  -?|-h list help          -a AND selections (OR)     -b avoid kernel blocks
  -c c  cmd c ^c /c/[bix]  +c w  COMMAND width (9)    +d s  dir s files
  -d s  select by FD set   +D D  dir D tree *SLOW?*   +|-e s  exempt s *RISKY*
  -i select IPv[46] files  -K list tasKs (threads)    -l list UID numbers
  -n no host names         -N select NFS files        -o list file offset
  -O no overhead *RISKY*   -P no port names           -R list paRent PID
  -s list file size        -t terse listing           -T disable TCP/TPI info
  -U select Unix socket    -v list version info       -V verbose search
  +|-w  Warnings (+)       -X skip TCP&UDP* files     -Z Z  context [Z]
  -- end option scan
  +f|-f  +filesystem or -file names     +|-f[gG] flaGs
  -F [f] select fields; -F? for help
  +|-L [l] list (+) suppress (-) link counts < l (0 = all; default = 0)
                                        +m [m] use|create mount supplement
  +|-M   portMap registration (-)       -o o   o 0t offset digits (8)
  -p s   exclude(^)|select PIDs         -S [t] t second stat timeout (15)
  -T qs TCP/TPI Q,St (s) info
  -g [s] exclude(^)|select and print process group IDs
  -i i   select by IPv[46] address: [46][proto][@host|addr][:svc_list|port_list]
  +|-r [t[m<fmt>]] repeat every t seconds (15);  + until no files, - forever.
       An optional suffix to t is m<fmt>; m must separate t from <fmt> and
      <fmt> is an strftime(3) format for the marker line.
  -s p:s  exclude(^)|select protocol (p = TCP|UDP) states by name(s).
  -u s   exclude(^)|select login|UID set s
  -x [fl] cross over +d|+D File systems or symbolic Links
  names  select named files or files on named file systems
Anyone can list all files; /dev warnings disabled; kernel ID check disabled.

5 ファイルをオープンしているプロセスを調べる方法

5.1 基本的な使い方

/var/log/messagesファイルに対して、lsofコマンドを実行してみます。 abrt-watch-logとrsyslogdの2つのプロセスが/var/log/messagesファイルをオープンしていることがわかります。 なお、プロセス名(COMMAND)の長さは、デフォルトで9文字です。そのため、abrt-watch-logがabrt-watcと切り詰められています。

[root@server ~]# lsof /var/log/messages
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
abrt-watc  643 root    4r   REG    8,3   330928 16878700 /var/log/messages
rsyslogd  1074 root    4w   REG    8,3   330928 16878700 /var/log/messages

次に、rsyslogサービスを停止します。

[root@server ~]# systemctl stop rsyslog.service

再度、lsofコマンドを実行します。 rsyslogdプロセスは、/var/log/messagesをオープンしていないことがわかります。

[root@server ~]# lsof /var/log/messages
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
abrt-watc 643 root    4r   REG    8,3   330928 16878700 /var/log/messages

5.2 ディレクトリを再帰的にたどる方法(+D)

+Dオプションを指定すると、指定したディレクト配下を再帰的にたどり、ファイルをオープンしているプロセスを表示します。

[root@server ~]# lsof +D /var/log/
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
auditd     598 root    5w   REG    8,3  2245418 33584848 /var/log/audit/audit.log
abrt-watc  643 root    4r   REG    8,3   337295 16878700 /var/log/messages
firewalld  745 root    3w   REG    8,3     6449 17913092 /var/log/firewalld
tuned     1073 root    3w   REG    8,3    16740 51319417 /var/log/tuned/tuned.log
cupsd     1079 root    6u   REG    8,3        0 51184781 /var/log/cups/access_log
cupsd     1079 root    7u   REG    8,3        0 51184782 /var/log/cups/error_log
cupsd     1079 root    8u   REG    8,3        0 51184783 /var/log/cups/page_log
rsyslogd  3327 root    6w   REG    8,3   337295 16878700 /var/log/messages
rsyslogd  3327 root    7w   REG    8,3     5134 16878701 /var/log/secure
rsyslogd  3327 root    8w   REG    8,3     4706 16878698 /var/log/cron

5.3 ディレクトリを再帰的にたどらない方法(+d)

+dオプションを指定すると、指定したディレクトに存在するファイルをオープンしているプロセスを表示します。

[root@server ~]# lsof +d /var/log/
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
abrt-watc  643 root    4r   REG    8,3   337499 16878700 /var/log/messages
firewalld  745 root    3w   REG    8,3     6449 17913092 /var/log/firewalld
rsyslogd  3327 root    6w   REG    8,3   337499 16878700 /var/log/messages
rsyslogd  3327 root    7w   REG    8,3     5134 16878701 /var/log/secure
rsyslogd  3327 root    8w   REG    8,3     4706 16878698 /var/log/cron

6 オープンしているファイルをユーザで絞り込む方法(-u)

6.1 特定のユーザが使用しているファイルを表示する方法

chronyユーザがオープンしているファイルを表示してみます。

[root@server ~]# lsof -u chrony
COMMAND PID   USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME
chronyd 667 chrony  cwd    DIR                8,3      264      64 /
chronyd 667 chrony  rtd    DIR                8,3      264      64 /
chronyd 667 chrony  txt    REG                8,3   269392  461392 /usr/sbin/chronyd
-snip-

次に、polkitdユーザがオープンしているファイルを表示してみます。

[root@server ~]# lsof -u polkitd
COMMAND PID    USER   FD      TYPE             DEVICE SIZE/OFF     NODE NAME
polkitd 627 polkitd  cwd       DIR                8,3      264       64 /
polkitd 627 polkitd  rtd       DIR                8,3      264       64 /
polkitd 627 polkitd  txt       REG                8,3   120432 34158507 /usr/lib/polkit-1/polkitd
-snip-

6.2 特定のユーザ以外のユーザが使用しているファイルを表示する方法

rootユーザ以外のユーザがオープンしているファイルを表示してみます。

[root@server ~]# lsof -u ^root
COMMAND    PID TID           USER   FD      TYPE             DEVICE SIZE/OFF     NODE NAME
lsmd       625     libstoragemgmt  cwd       DIR                8,3      264       64 /
lsmd       625     libstoragemgmt  rtd       DIR                8,3      264       64 /
lsmd       625     libstoragemgmt  txt       REG                8,3    24016 56028264 /usr/bin/lsmd
-snip-

7 オープンしているファイルをFDで絞り込む方法

chronyユーザがオープンしているファイルの中で、FD=8のファイルを表示してみます。 -uでユーザを指定し、-dでFD(File Descriptor)を指定します。-aは-uと-dのAND条件を指定するために使用しています。 lsofの実行結果より、chronyユーザは、/var/run/chrony/chronyd.sockをFD=8でオープンしていることがわかります。

[root@server ~]#  lsof -u chrony -a -d 8
COMMAND PID   USER   FD   TYPE             DEVICE SIZE/OFF  NODE NAME
chronyd 667 chrony    8u  unix 0xffff9e0bf5790440      0t0 20186 /var/run/chrony/chronyd.sock

8 プロセス名の表示幅を広げる方法(+c)

lsofの実行結果に表示されるプロセス名(COMMAND)の幅はデフォルトで9文字です。 +cオプションを指定することで、幅を広げることができます。

+c0を指定すると、プロセス名が途中で切り捨てられることなく全て表示されます。

[root@server ~]# lsof +c0 /var/log/messages
COMMAND         PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
abrt-watch-log  643 root    4r   REG    8,3   340736 16878700 /var/log/messages
rsyslogd       3327 root    6w   REG    8,3   340736 16878700 /var/log/messages

次に、+c10を指定してみます。 abrt-watch-logプロセス名がデフォルト表示のabrt-watcからabrt-watchに1文字多く表示されるようになったことがわかります。

[root@server ~]# lsof +c10 /var/log/messages
COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
abrt-watch  643 root    4r   REG    8,3   340869 16878700 /var/log/messages
rsyslogd   3327 root    6w   REG    8,3   340869 16878700 /var/log/messages

9 INETドメインソケットに関する使いかた

9.1 IPV4,IPv6ソケットを使用しているプロセスを絞り込む方法(-i)

-iオプションを使うと、IPV4,IPv6ソケットを使用しているプロセスを表示できます。 rpcbindプロセスは、IPV4,IPv6ソケットを使用していることがわかります。

[root@server ~]# lsof -i
COMMAND    PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
rpcbind    624    rpc    6u  IPv4  19549      0t0  UDP *:sunrpc
rpcbind    624    rpc    7u  IPv4  19550      0t0  UDP *:798
rpcbind    624    rpc    8u  IPv4  19551      0t0  TCP *:sunrpc (LISTEN)
rpcbind    624    rpc    9u  IPv6  19552      0t0  UDP *:sunrpc
rpcbind    624    rpc   10u  IPv6  19553      0t0  UDP *:798
rpcbind    624    rpc   11u  IPv6  19554      0t0  TCP *:sunrpc (LISTEN)
-snip-

9.2 TCPソケットを使用しているプロセスを絞り込む方法(-i)

-iまたはiTCPとオプションを指定することで、TCPのソケットを使用しているプロセスを表示することができます。

[root@server ~]# lsof -iTCP
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
rpcbind  624    rpc    8u  IPv4  19551      0t0  TCP *:sunrpc (LISTEN)
rpcbind  624    rpc   11u  IPv6  19554      0t0  TCP *:sunrpc (LISTEN)
sshd    1040   root    3u  IPv4  25400      0t0  TCP *:ssh (LISTEN)
sshd    1040   root    4u  IPv6  25402      0t0  TCP *:ssh (LISTEN)
cupsd   1043   root   10u  IPv6  25413      0t0  TCP localhost:ipp (LISTEN)
cupsd   1043   root   11u  IPv4  25414      0t0  TCP localhost:ipp (LISTEN)
master  1344   root   13u  IPv4  27774      0t0  TCP localhost:smtp (LISTEN)
master  1344   root   14u  IPv6  27775      0t0  TCP localhost:smtp (LISTEN)
dnsmasq 1399 nobody    6u  IPv4  23540      0t0  TCP server:domain (LISTEN)
sshd    1708   root    3u  IPv4  28102      0t0  TCP server:ssh->DESKTOP-MB6G4KI:53893 (ESTABLISHED)

9.3 UDPソケットを使用しているプロセスを絞り込む方法(-iUDP)

-iUDPとオプションを指定することで、UDPのソケットを使用しているプロセスを表示することができます。

[root@server ~]# lsof -iUDP
COMMAND    PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
rpcbind    624    rpc    6u  IPv4  19549      0t0  UDP *:sunrpc
rpcbind    624    rpc    7u  IPv4  19550      0t0  UDP *:798
rpcbind    624    rpc    9u  IPv6  19552      0t0  UDP *:sunrpc
rpcbind    624    rpc   10u  IPv6  19553      0t0  UDP *:798
avahi-dae  647  avahi   12u  IPv4  20389      0t0  UDP *:mdns
avahi-dae  647  avahi   13u  IPv4  20390      0t0  UDP *:51640
chronyd    678 chrony    5u  IPv4  23578      0t0  UDP localhost:323
chronyd    678 chrony    6u  IPv6  23579      0t0  UDP localhost:323
dhclient   849   root    6u  IPv4  22863      0t0  UDP *:bootpc
dnsmasq   1399 nobody    3u  IPv4  23536      0t0  UDP *:bootps
dnsmasq   1399 nobody    5u  IPv4  23539      0t0  UDP server:domain

9.4 ポート番号で使用しているプロセスを絞り込む方法

TCPの22番ポートを使用しているプロセスを表示してます。 sshdプロセスがTCPの22番ポートを使用していることがわかります。 なお、-Pオプションを指定すると、sshのようなサービス名ではなくポート番号を表示することができます。

[root@server ~]# lsof -i:22 -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    1092 root    3u  IPv4  27698      0t0  TCP *:22 (LISTEN)
sshd    1092 root    4u  IPv6  27700      0t0  TCP *:22 (LISTEN)
sshd    1801 root    3u  IPv4  28373      0t0  TCP server:22->DESKTOP-MB6G4KI:60395 (ESTABLISHED)

ポート番号の範囲を指定することもできます。 TCPの1番ポートから50番ポートを使用しているプロセスを表示してみます。

[root@server ~]# lsof -i:1-50 -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    1092 root    3u  IPv4  27698      0t0  TCP *:22 (LISTEN)
sshd    1092 root    4u  IPv6  27700      0t0  TCP *:22 (LISTEN)
master  1309 root   13u  IPv4  28815      0t0  TCP localhost:25 (LISTEN)
master  1309 root   14u  IPv6  28816      0t0  TCP localhost:25 (LISTEN)
sshd    1801 root    3u  IPv4  28373      0t0  TCP server:22->DESKTOP-MB6G4KI:60395 (ESTABLISHED)

9.5 IPアドレスでプロセスを絞り込む方法

[root@server ~]# lsof -i@192.168.2.100 -n
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    1801 root    3u  IPv4  28373      0t0  TCP 192.168.2.100:ssh->192.168.2.12:60395 (ESTABLISHED)

9.6 TCPの状態でプロセスを絞り込む方法

TCPの状態がESTABLISHEDのプロセスを表示してみます。

[root@server ~]# lsof -i -sTCP:ESTABLISHED -n
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    1801 root    3u  IPv4  28373      0t0  TCP 192.168.2.100:ssh->192.168.2.12:60395 (ESTABLISHED)

次は、TCPの状態がLISTENのプロセスを表示してみます。

[root@server ~]# lsof -i -sTCP:LISTEN -n
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
rpcbind  647    rpc    8u  IPv4  20029      0t0  TCP *:sunrpc (LISTEN)
rpcbind  647    rpc   11u  IPv6  20032      0t0  TCP *:sunrpc (LISTEN)
cupsd   1085   root   10u  IPv6  28744      0t0  TCP [::1]:ipp (LISTEN)
cupsd   1085   root   11u  IPv4  28745      0t0  TCP 127.0.0.1:ipp (LISTEN)
sshd    1092   root    3u  IPv4  27698      0t0  TCP *:ssh (LISTEN)
sshd    1092   root    4u  IPv6  27700      0t0  TCP *:ssh (LISTEN)
master  1309   root   13u  IPv4  28815      0t0  TCP 127.0.0.1:smtp (LISTEN)
master  1309   root   14u  IPv6  28816      0t0  TCP [::1]:smtp (LISTEN)
dnsmasq 1714 nobody    6u  IPv4  28089      0t0  TCP 192.168.122.1:domain (LISTEN)

10 リピートモードの使い方

ncプロセス(-c)が使用するINETドメイン(-i)のソケットを2秒間隔(-r2)で監視してみます。 -mはマーカです。%Tは時刻を表示します。 また、-aオプションを使って3つの条件をAND条件で指定しています。

lsofコマンドを実行します。 なお、ncコマンドの使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してくだい。

[root@server ~]# lsof -c nc -a -i4 -a -P -a -r2m====%T====
====17:06:14====
====17:06:16====

もう1つターミナルを開きます。そしてncコマンドを実行します。

[root@server ~]# nc -kl 11111

lsofコマンドの実行結果を確認します。 ncプロセスのTCPの状態がLISTENになったことがわかります。

[root@server ~]# lsof -c nc -a -i4 -a -P -a -r2m====%T====
====17:08:50====
====17:08:52====
====17:08:54====
====17:08:56====
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      3500 root    4u  IPv4  46030      0t0  TCP *:11111 (LISTEN)
====17:08:58====
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      3500 root    4u  IPv4  46030      0t0  TCP *:11111 (LISTEN)
-snip-

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

[root@client ~]# nc 192.168.2.100 11111

lsofコマンドの実行結果を確認します。 ncプロセスのTCPの状態がLISTENからESTABLISHEDになったことがわかります。

[root@server ~]# lsof -c nc -a -i4 -a -P -a -r2m====%T====
-snip-
====17:11:25====
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      3500 root    4u  IPv4  46030      0t0  TCP *:11111 (LISTEN)
nc      3500 root    5u  IPv4  56384      0t0  TCP server:11111->client:43798 (ESTABLISHED)

11 ソケットの状態を表示する方法

IPv4TCPソケットの情報を表示してみます。

[root@server ~]# lsof -i4TCP -Tqs
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
rpcbind  647    rpc    8u  IPv4  20029      0t0  TCP *:sunrpc (LISTEN QR=0 QS=0)
cupsd   1085   root   11u  IPv4  28745      0t0  TCP localhost:ipp (LISTEN QR=0 QS=0)
sshd    1092   root    3u  IPv4  27698      0t0  TCP *:ssh (LISTEN QR=0 QS=0)
master  1309   root   13u  IPv4  28815      0t0  TCP localhost:smtp (LISTEN QR=0 QS=0)
dnsmasq 1714 nobody    6u  IPv4  28089      0t0  TCP server:domain (LISTEN QR=0 QS=0)
sshd    1801   root    3u  IPv4  28373      0t0  TCP server:ssh->DESKTOP-MB6G4KI:60395 (ESTABLISHED QR=0 QS=36)

なお、上記実行結果中のQR、QSの意味は以下のとおりです。

QRカーネル受信バッファに存在する受信データのバイト数。ユーザプロセスがまだ読み出していない受信データです。

QS:カーネル送信バッファに存在する送信データのバイト数。まだACKを受信しておらず、開放されていない送信データです。

QRについて、以下のような実験をして確認してみます。 まず、サーバでncコマンドを実行します。

[root@server ~]# nc -kl 11111

lsofコマンドを実行します。QRの値は0であることがわかります。

[root@server ~]#  lsof -c nc -i4TCP -a -P -a -Tqs
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      3683 root    4u  IPv4  56710      0t0  TCP *:11111 (LISTEN QR=0 QS=0)

クライアントでncコマンドを実行します。 ncコマンドを実行すると、サーバのncプロセスとの間でTCPコネクションが確立されます。

[root@client ~]# nc 192.168.2.100 11111

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

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

クライアントで12345と入力します。改行コードも含めて6バイトがサーバに送信されます。

[root@client ~]# nc 192.168.2.100 11111
12345

lsofコマンドを実行します。QRの値が6になったことがわかります。

[root@server ~]#  lsof -c nc -i4TCP -a -P -a -Tqs
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      3683 root    4u  IPv4  56710      0t0  TCP *:11111 (LISTEN QR=0 QS=0)
nc      3683 root    5u  IPv4  58859      0t0  TCP server:11111->client:43806 (ESTABLISHED QR=6 QS=0)

次にfgコマンドを実行して、サーバのncプロセスを再開します。 ncプロセスが再開すると、カーネルの受信バッファから受信データを読み出します。

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

再度、lsofコマンドを実行します。QRの値が0になったことがわかります。 これは、ncプロセスを再開したことで、ncプロセスがカーネル受信バッファにたまっていた6バイトのデータを読み出したからです。

[root@server ~]#  lsof -c nc -i4TCP -a -P -a -Tqs
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      3683 root    4u  IPv4  56710      0t0  TCP *:11111 (LISTEN QR=0 QS=0)
nc      3683 root    5u  IPv4  58859      0t0  TCP server:11111->client:43806 (ESTABLISHED QR=0 QS=0)

Z 参考情報

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

straceコマンドの使い方

1 straceコマンドとは?

プロセスが実行するシステムコールをトレースするコマンドです。なお、関数のトレースには、ltraceコマンドを使います。ltraceコマンドは、ltraceコマンドの使い方 - 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 インストール方法

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

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

版数を確認します。

[root@server ~]# strace -V
strace -- version 4.24

4 オプション一覧

[root@server ~]# strace -h
usage: strace [-CdffhiqrtttTvVwxxy] [-I n] [-e expr]...
              [-a column] [-o file] [-s strsize] [-P path]...
              -p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]
   or: strace -c[dfw] [-I n] [-e expr]... [-O overhead] [-S sortby]
              -p pid... / [-D] [-E var=val]... [-u username] PROG [ARGS]

Output format:
  -a column      alignment COLUMN for printing syscall results (default 40)
  -i             print instruction pointer at time of syscall
  -k             obtain stack trace between each syscall
  -o file        send trace output to FILE instead of stderr
  -q             suppress messages about attaching, detaching, etc.
  -r             print relative timestamp
  -s strsize     limit length of print strings to STRSIZE chars (default 32)
  -t             print absolute timestamp
  -tt            print absolute timestamp with usecs
  -T             print time spent in each syscall
  -x             print non-ascii strings in hex
  -xx            print all strings in hex
  -X format      set the format for printing of named constants and flags
  -y             print paths associated with file descriptor arguments
  -yy            print protocol specific information associated with socket file descriptors

Statistics:
  -c             count time, calls, and errors for each syscall and report summary
  -C             like -c but also print regular output
  -O overhead    set overhead for tracing syscalls to OVERHEAD usecs
  -S sortby      sort syscall counts by: time, calls, name, nothing (default time)
  -w             summarise syscall latency (default is system time)

Filtering:
  -e expr        a qualifying expression: option=[!]all or option=[!]val1[,val2]...
     options:    trace, abbrev, verbose, raw, signal, read, write, fault, inject, kvm
  -P path        trace accesses to path

Tracing:
  -b execve      detach on execve syscall
  -D             run tracer process as a detached grandchild, not as parent
  -f             follow forks
  -ff            follow forks with output into separate files
  -I interruptible
     1:          no signals are blocked
     2:          fatal signals are blocked while decoding syscall (default)
     3:          fatal signals are always blocked (default if '-o FILE PROG')
     4:          fatal signals and SIGTSTP (^Z) are always blocked
                 (useful to make 'strace -o FILE PROG' not stop on ^Z)

Startup:
  -E var         remove var from the environment for command
  -E var=val     put var=val in the environment for command
  -p pid         trace process with process id PID, may be repeated
  -u username    run command as username handling setuid and/or setgid

Miscellaneous:
  -d             enable debug output to stderr
  -v             verbose mode: print unabbreviated argv, stat, termios, etc. args
  -h             print help message
  -V             print version

5 システムコールの実行順序を表示する方法

5.1 事前準備

straceの対象となるテストプログラムを作成します。

[root@master1 strace]# vi tp.c
[root@master1 strace]# cat tp.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>

int main(int argc, char *argv[])
{
    int fd;
    struct timespec req;
    char str[] = "0123456789";

    req.tv_sec = 2;
    req.tv_nsec = 0;

    for(;;) {
        fd = open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
        write(fd, str, (size_t)sizeof(str));
        close(fd);
        nanosleep(&req, NULL);
    }
}

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

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

実行ファイルを確認します。tpという名前の実行ファイルが作成されたことがわかります。

[root@server ~]# ls -l tp*
-rwxr-xr-x. 1 root root 8552 12月 26 09:11 tp
-rw-r--r--. 1 root root  425 12月 26 09:11 tp.c

5.2 実行結果

テストプログラムを実行します。

[root@server ~]# ./tp

もう1つターミナルを開きます。 テストプログラムのPIDを確認すると、PID=2357であることがわかります。

[root@server ~]# ps -C tp
   PID TTY          TIME CMD
  2357 pts/0    00:00:00 tp

straceを実行します。-pはテストプログラムのPIDを指定します。 open,write,close,nanosleepの順にシステムコールを実行していることがわかります。

[root@server ~]# strace -p 2357
strace: Process 2357 attached
restart_syscall(<... resuming interrupted read ...>) = 0
open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, "0123456789\0", 11)            = 11
close(3)                                = 0
nanosleep({tv_sec=2, tv_nsec=0}, NULL)  = 0
-snip-

6 時刻関連

6.1 システムコール実行時刻(秒単位)を表示する方法(-t)

-tはシステムコールの実行時刻を秒単位で表示(左端の時刻)するオプションです。

[root@server ~]# strace -t -p 2357
strace: Process 2357 attached
09:35:32 restart_syscall(<... resuming interrupted read ...>) = 0
09:35:33 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
09:35:33 write(3, "0123456789\0", 11)   = 11
09:35:33 close(3)                       = 0
09:35:33 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
-snip-

6.2 システムコール実行時刻(マイクロ秒単位)を表示する方法(-tt)

-ttはシステムコールの実行時刻をマイクロ秒単位で表示(左端の時刻)するオプションです。

[root@server ~]# strace -tt -p 2357
strace: Process 2357 attached
09:36:31.183463 restart_syscall(<... resuming interrupted read ...>) = 0
09:36:31.953965 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
09:36:31.958852 write(3, "0123456789\0", 11) = 11
09:36:31.960077 close(3)                = 0
09:36:31.960550 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
-snip-

6.3 システムコール実行時間を表示する方法(-T)

-Tはシステムコールを実行してから完了するまでの実行時間を表示するオプションです。右端の<>に囲まれた部分が、システムコールの実行時間を表していて、マイクロ秒単位の精度で表示されていることがわかります。また、nanosleepシステムコールは、スリープ時間に指定した2秒より少し時間がかかってシステムコールが完了していることがわかります(★印)。

[root@server ~]# strace -T -p 2357
strace: Process 2357 attached
restart_syscall(<... resuming interrupted read ...>) = 0 <0.656177>
open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 <0.002957>
write(3, "0123456789\0", 11)            = 11 <0.000485>
close(3)                                = 0 <0.000868>
nanosleep({tv_sec=2, tv_nsec=0}, NULL)  = 0 ★<2.010235>
open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 <0.003676>
write(3, "0123456789\0", 11)            = 11 <0.000451>
close(3)                                = 0 <0.000254>
nanosleep({tv_sec=2, tv_nsec=0}, NULL)  = 0 ★<2.003008>
-snip-

6.4 システムコールの実行間隔を表示する方法(-r)

-rはシステムコールの実行間隔を表示するオプションです。時刻T1と時刻T2にシステムコールを実行したとき、システムコールの実行間隔(T2-T1)を表示します。

[root@server ~]# strace -r -p 2357
strace: Process 2357 attached
     0.000000 restart_syscall(<... resuming interrupted read ...>) = 0
     0.493108 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
     0.000888 write(3, "0123456789\0", 11) = 11
     0.000647 close(3)                  = 0
     0.000977 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
     2.004976 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
     0.005188 write(3, "0123456789\0", 11) = 11
     0.000544 close(3)                  = 0
     0.000317 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
     2.003285 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
     0.000197 write(3, "0123456789\0", 11) = 11
     0.000096 close(3)                  = 0
     0.000359 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
-snip-

7 実行結果を絞り込む方法(-e trace)

7.1 特定のシステムコールを表示する方法(-e trace=システムコール名)

openシステムコールだけを表示してみます。

[root@server ~]# strace -t -e trace=open -p 2357
strace: Process 2357 attached
10:33:39 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
10:33:41 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
10:33:43 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
-snip-

openシステムコールとcloseシステムコールを表示してみます。

[root@server ~]# strace -t -e trace=open,close -p 2357
strace: Process 2357 attached
10:34:49 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
10:34:49 close(3)                       = 0
10:34:51 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
10:34:51 close(3)                       = 0
-snip-

7.2 特定のシステムコールをstrace実行結果から除外する方法(-e 'trace=!システムコール名')

write以外のシステムコールを表示してみます。

[root@server ~]# strace -t -e 'trace=!write' -p 2357
strace: Process 2357 attached
10:36:31 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
10:36:31 close(3)                       = 0
10:36:31 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
10:36:33 open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
10:36:33 close(3)                       = 0
10:36:33 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
-snip-

7.3 複数のシステムコールをstrace実行結果から除外する方法(-e 'trace=!システムコール名,システムコール名')

openとclose以外のシステムコールを表示してみます。

[root@server ~]# strace -t -e 'trace=!open,close' -p 2357
strace: Process 2357 attached
10:38:03 restart_syscall(<... resuming interrupted read ...>) = 0
10:38:04 write(3, "0123456789\0", 11)   = 11
10:38:04 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
10:38:06 write(3, "0123456789\0", 11)   = 11
10:38:06 nanosleep({tv_sec=2, tv_nsec=0}, NULL) = 0
-snip-

7.4 ネットワーク関連のシステムコールを表示する方法(-e trace=%network)

digコマンドを使って、首相官邸のサーバにアクセスします。 socketやgetsockname等のネットワーク関連のシステムコールが表示されていることがわかります。

[root@server ~]# strace -e trace=%network dig www.kantei.go.jp +short
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 3
getsockname(3, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 0
socket(AF_UNIX, SOCK_STREAM, 0)         = 3
13.33.9.26
13.33.9.75
13.33.9.99
13.33.9.45
--- SIGTERM {si_signo=SIGTERM, si_code=SI_TKILL, si_pid=3098, si_uid=0} ---
+++ exited with 0 +++

7.5 シグナルのシステムコールだけを表示する方法(-e trace=%signal)

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

[root@server ~]# sleep 300&
[1] 1820

プロセスの状態を確認します。

[root@server ~]#  ps -C sleep
   PID TTY          TIME CMD
  1820 pts/0    00:00:00 sleep

シグナルのシステムコールだけを表示してみます。killシステムコールが実行されていることがわかります。

[root@server ~]# strace -e trace=%signal kill -9 1820
kill(1820, SIGKILL)                     = 0
+++ exited with 0 +++
[1]+  強制終了            sleep 300

8 指定ファイルをアクセスしたときにトレースを表示する方法(-P)

8.1 事前準備

テストプログラムを作成します。

[root@server ~]# vi tp1.c
[root@server ~]# cat tp.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

int main(int argc, char *argv[])
{
    int fd;
    struct timespec req;

    req.tv_sec = 1;
    req.tv_nsec = 0;
    printf("path=%s\n", argv[1]);

    for(;;) {
        fd = open(argv[1], O_RDONLY, 0444);
        close(fd);
        nanosleep(&req, NULL);
    }
}

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

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

作成したテストプログラムを確認します。

[root@server ~]# ls -l tp*
-rwxr-xr-x. 1 root root 8552 12月 26 11:04 tp
-rw-r--r--. 1 root root  343 12月 26 11:02 tp.c

上記で作成したテストプログラムが参照するテスト用ファイルを作成します。

[root@server ~]# touch /tmp/test.txt

8.2 実行結果

テストプログラムを実行します。このときアクセスするファイルは/tmp/test.txtを指定します。

[root@server ~]# ./tp /tmp/test.txt
path=/tmp/test.txt

もう1つターミナルを開いて、テストプログラムのPIDを確認します。

[root@server ~]# ps -C tp
   PID TTY          TIME CMD
  1882 pts/0    00:00:00 tp

straceを実行します。このとき、-Pのパラメータとして、/tmp/test.txtを指定します。 /tmp/test.txtにアクセスするたびに、straceが実行結果を表示していることがわかる。

[root@server ~]# strace -t -P /tmp/test.txt -p 1882
strace: Process 1882 attached
11:10:43 open("/tmp/test.txt", O_RDONLY) = 3
11:10:43 close(3)                       = 0
11:10:44 open("/tmp/test.txt", O_RDONLY) = 3
11:10:44 close(3)                       = 0
11:10:45 open("/tmp/test.txt", O_RDONLY) = 3
11:10:45 close(3)                       = 0
-snip-

9 複数プロセス/スレッドに対してstraceを実行する方法

9.1 動作中の複数プロセス対するstrace実行方法(-p pid,pid,...)

httpdを起動します。

[root@server ~]# systemctl start httpd

psコマンドを実行すると、PID=5797が親プロセス、他は子プロセスであることがわかります。

[root@server ~]# ps -C httpd -o comm,pid,ppid
COMMAND            PID   PPID
httpd             5797      1
httpd             5798   5797
httpd             5799   5797
httpd             5800   5797
httpd             5801   5797
httpd             5802   5797

straceを実行します。

[root@server ~]# strace -t -p 5797,5798,5799,5800,5801,5802
strace: Process 5797 attached
strace: Process 5798 attached
strace: Process 5799 attached
strace: Process 5800 attached
strace: Process 5801 attached
strace: Process 5802 attached

httpdの80番ポートを開放します。

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

curlhttpdにアクセスします。

[root@client ~]# curl -I http://192.168.2.100

親プロセスはselect,wait4を繰り返し実行しています。クライアントでcurlコマンドを実行すると、子プロセス(PID=5802)が"/var/www/html/index.html"を読み込んでいることがわかります。

[root@server ~]# strace -t -p 5797,5798,5799,5800,5801,5802
-snip-
[pid  5797] 20:56:06 select(0, NULL, NULL, NULL, {tv_sec=1, tv_usec=0}) = 0 (Timeout)
[pid  5797] 20:56:07 wait4(-1, 0x7ffda9db874c, WNOHANG|WSTOPPED, NULL) = 0
[pid  5797] 20:56:07 select(0, NULL, NULL, NULL, {tv_sec=1, tv_usec=0}) = 0 (Timeout)
[pid  5797] 20:56:08 wait4(-1, 0x7ffda9db874c, WNOHANG|WSTOPPED, NULL) = 0
[pid  5797] 20:56:08 select(0, NULL, NULL, NULL, {tv_sec=1, tv_usec=0} <unfinished ...>
[pid  5802] 20:56:09 <... accept4 resumed>{sa_family=AF_INET6, sin6_port=htons(36844), inet_pton(AF_INET6, "::ffff:192.168.2.105", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [128->28], SOCK_CLOEXEC) = 9
[pid  5802] 20:56:09 getsockname(9, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:192.168.2.100", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [128->28]) = 0
[pid  5802] 20:56:09 fcntl(9, F_GETFL)  = 0x2 (flags O_RDWR)
[pid  5802] 20:56:09 fcntl(9, F_SETFL, O_RDWR|O_NONBLOCK) = 0
[pid  5802] 20:56:09 read(9, "HEAD / HTTP/1.1\r\nUser-Agent: cur"..., 8000) = 78
[pid  5802] 20:56:09 stat("/var/www/html/", {st_mode=S_IFDIR|0755, st_size=24, ...}) = 0
[pid  5802] 20:56:09 stat("/var/www/html/index.html", {st_mode=S_IFREG|0644, st_size=12, ...}) = 0
[pid  5802] 20:56:09 open("/var/www/html/index.html", O_RDONLY|O_CLOEXEC) = 10

9.2 子プロセスもstraceの対象にする方法(-f)

-fオプションは、親プロセスから生成する子プロセスもstraceの対象にします。 ここでは、親プロセスから生成するスレッドをstraceの対象にしてみます。

1プロセスに2つのスレッドを生成するテストプログラムを作成します。

[root@server ~]# cat thread.c
#include <stdio.h>
#include <pthread.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

void *threadFunc1(void)
{
    int fd;
    char str[] = "0123456789";

    for(;;) {
        fd = open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);

        write(fd, str, (size_t)sizeof(str));
        close(fd);
        sleep(3);
    }
}

int main(int argc, char *argv[])
{
    pthread_t t1;
    void *res;

    printf("PID=%d\n",getpid());
    sleep(10);

    pthread_create(&t1, NULL, (void *)&threadFunc1, NULL);

    pthread_join(t1, &res);
    _exit(0);
}

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

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

作成したテストプログラムを確認します。

[root@server ~]# ls -l thread*
-rwxr-xr-x. 1 root root 8760  1月  8 21:29 thread
-rw-r--r--. 1 root root  632  1月  8 21:28 thread.c

テストプログラムを起動します。

[root@server ~]# ./thread
PID=10493

psコマンドを実行します。threadのPIDはともに10493 ですが、LWPはそれぞれ10493,10496となっていて、スレッドであることがわかります。

[root@server ~]# ps -LC thread
   PID    LWP TTY          TIME CMD
 10493  10493 pts/0    00:00:00 thread
 10493  10496 pts/0    00:00:00 thread

実行結果を確認すると、親プロセスから起動したスレッドのstraceが表示されているのがわかります。 なお、実行結果のpidはpsコマンドのPIDではなくLWPを表していると思われます。

[root@server ~]# strace -f -p 10493
strace: Process 10493 attached
restart_syscall(<... resuming interrupted read ...>) = 0
mmap(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f7cc7053000
brk(NULL)                               = 0x8d2000
brk(0x8f3000)                           = 0x8f3000
brk(NULL)                               = 0x8f3000
mprotect(0x7f7cc7053000, 4096, PROT_NONE) = 0
clone(child_stack=0x7f7cc7852fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f7cc78539d0, tls=0x7f7cc7853700, child_tidptr=0x7f7cc78539d0) = 10496
strace: Process 10496 attached
[pid 10493] futex(0x7f7cc78539d0, FUTEX_WAIT, 10496, NULL <unfinished ...>
[pid 10496] set_robust_list(0x7f7cc78539e0, 24) = 0
[pid 10496] open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[pid 10496] write(3, "0123456789\0", 11) = 11
[pid 10496] close(3)                    = 0
-snip-

10 表示する文字列数を調整する方法(-s)

-sはシステムコールの引数に指定する文字数を指定するオプションです。デフォルトは32文字です。

10.1 事前準備

5.1で作成したテストプログラムを改造します。改造部分は、char str[]に定義する文字列です。文字列を10文字から40文字に変更しました。

[root@server ~]# vi tp.c
[root@server ~]# cat tp.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>

int main(int argc, char *argv[])
{
    int fd;
    struct timespec req;
    char str[] = "0123456789012345678901234567890123456789";

    req.tv_sec = 2;
    req.tv_nsec = 0;

    for(;;) {
        fd = open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
        write(fd, str, (size_t)sizeof(str));
        close(fd);
        nanosleep(&req, NULL);
    }
}

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

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

作成したテストプログラムを確認します。

[root@server ~]# ls -l tp*
-rwxr-xr-x. 1 root root 8552 12月 26 11:21 tp
-rw-r--r--. 1 root root  456 12月 26 11:19 tp.c

10.2 実験結果

テストプログラムを実行します。

[root@server ~]# ./tp

もう1つターミナルを開きます。 テストプログラムのPIDを確認すると、PID=2029であることがわかります。

[root@server ~]# ps -C tp
   PID TTY          TIME CMD
  2029 pts/1    00:00:00 tp

まずオプション(-s)指定なしてstraceを実行してみます。 writeシステムコールの第2引数は32バイトまでしか表示されていないことがわかります。

[root@server ~]# strace -p 2029
strace: Process 2029 attached
restart_syscall(<... resuming interrupted read ...>) = 0
open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, "01234567890123456789012345678901"..., 41) = 41
close(3)                                = 0
nanosleep({tv_sec=2, tv_nsec=0}, NULL)  = 0
-snip-

次に-sオプションを指定して、straceを実行してみます。 writeシステムコールの第2引数が省略されず、40バイト全て表示されていることがわかります。

[root@server ~]# strace -s 41 -p 2029
strace: Process 2029 attached
restart_syscall(<... resuming interrupted read ...>) = 0
open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, "0123456789012345678901234567890123456789\0", 41) = 41
close(3)                                = 0
nanosleep({tv_sec=2, tv_nsec=0}, NULL)  = 0
-snip-

11 strace実行結果をファイルに保存する方法(-o)

11.1 実行結果を1つにまとめる方法

テストプログラムを実行します。

[root@server ~]# ./tp

straceの対象となるプロセスのPIDを確認します。

[root@server ~]# ps -C tp
   PID TTY          TIME CMD
  2510 pts/1    00:00:00 tp

-oオプションを指定して、straceを実行します。

[root@server ~]# strace -p 2510 -o strace.log
strace: Process 2510 attached

Ctrl+cを押下して、straceを終了します。そして、保存した結果を確認します。

[root@server ~]# cat strace.log
open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, "01234567890123456789012345678901"..., 41) = 41
close(3)                                = 0
nanosleep({tv_sec=2, tv_nsec=0}, NULL)  = 0
-snip-

11.2 実行結果をプロセス毎にわける方法(-ff)

httpdを起動します。

[root@server ~]# systemctl start httpd

httpdのPIDを確認します。

[root@server ~]# ps -C httpd
   PID TTY          TIME CMD
  2418 ?        00:00:00 httpd
  2419 ?        00:00:00 httpd
  2420 ?        00:00:00 httpd
  2421 ?        00:00:00 httpd
  2422 ?        00:00:00 httpd
  2423 ?        00:00:00 httpd

-oオプションを指定して、straceを実行します。

[root@server ~]# strace -ff -p 2418,2419,2420,2421,2422,2423 -o strace.log
strace: Process 2418 attached
strace: Process 2419 attached
strace: Process 2420 attached
strace: Process 2421 attached
strace: Process 2422 attached
strace: Process 2423 attached

Ctrl+cを押下して、straceを終了します。PID毎に実行結果が保存できたことがわかります。

[root@server ~]# ls -l strace.log.*
-rw-r--r--. 1 root root 1259  1月  6 21:29 strace.log.2418
-rw-r--r--. 1 root root   27  1月  6 21:29 strace.log.2419
-rw-r--r--. 1 root root   27  1月  6 21:29 strace.log.2420
-rw-r--r--. 1 root root   27  1月  6 21:29 strace.log.2421
-rw-r--r--. 1 root root   27  1月  6 21:29 strace.log.2422
-rw-r--r--. 1 root root   27  1月  6 21:29 strace.log.2423
[root@server ~]# cat strace.log.2418
select(0, NULL, NULL, NULL, {tv_sec=0, tv_usec=414792}) = 0 (Timeout)
socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 8
getsockopt(8, SOL_SOCKET, SO_SNDBUF, [212992], [4]) = 0
setsockopt(8, SOL_SOCKET, SO_SNDBUFFORCE, [8388608], 4) = -1 EPERM (許可されていない操作です)
setsockopt(8, SOL_SOCKET, SO_SNDBUF, [8388608], 4) = 0
-snip-

12 UNIXドメインソケットを監視する方法

12.1 事前準備

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

[root@server ~]# cat sv.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>

#define FILEPATH "/tmp/afunix_test"
#define BUF_SIZE 100

int main(int argc, char *argv[])
{
    int sfd, cfd;
    struct sockaddr_un addr;
    ssize_t numRead;
    char buf[BUF_SIZE];

    sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    remove(FILEPATH);
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;

    strncpy(addr.sun_path, FILEPATH, sizeof(addr.sun_path));
    bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un));

    listen(sfd, 5);

    for(;;){
        cfd = accept(sfd, NULL, NULL);
            while((numRead = read(cfd, buf, BUF_SIZE)) >0) {
                write(STDOUT_FILENO, buf, numRead);
            }
        close(cfd);
    }
}

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

[root@server ~]# cat cl.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>

#define FILEPATH "/tmp/afunix_test"
#define BUF_SIZE 100

int main(int argc, char *argv[])
{
    struct sockaddr_un addr;
    int sfd;
    ssize_t numRead;
    char buf[BUF_SIZE];

    sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, FILEPATH, sizeof(addr.sun_path));
    connect(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un));

    while((numRead=read(STDIN_FILENO, buf, BUF_SIZE)) > 0) {
        write(sfd, buf, numRead);
    }
    _exit(0);
}

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

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

テストプログラムを確認します。

[root@server ~]# ls -l sv* cl*
-rwxr-xr-x. 1 root root    8704  1月  6 21:46 cl
-rw-r--r--. 1 root root     661  1月  6 21:45 cl.c
-rwxr-xr-x. 1 root root    8856  1月  6 21:46 sv
-rw-r--r--. 1 root root     796  1月  6 21:44 sv.c

12.2 実験結果

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

[root@server ~]# ./sv

UNIXドメインソケットを確認します。 テストプログラム(sv)が使用しているUNIXドメインソケットが確認できます。

[root@server ~]# lsof -c sv -a -U
COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF  NODE NAME
sv      3346 root    3u  unix 0xffff8f1e70934400      0t0 43435 /tmp/afunix_test

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

[root@server ~]# ./cl

UNIXドメインソケットを確認します。 テストプログラム(cl)が使用しているUNIXドメインソケットが確認できます。

[root@server ~]# lsof -c sv -c cl -a -U
COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF  NODE NAME
sv      3346 root    3u  unix 0xffff8f1e70934400      0t0 43435 /tmp/afunix_test
sv      3346 root    4u  unix 0xffff8f1e717b2a80      0t0 43436 /tmp/afunix_test
cl      3423 root    3u  unix 0xffff8f1e717b1100      0t0 41286 socket

テストプログラムのPIDを確認します。

[root@server ~]# ps -C sv
   PID TTY          TIME CMD
  3346 pts/1    00:00:00 sv
[root@server ~]#  strace -f -e trace=read,write -e read=3,4 -e write=3,4 -p 3346
strace: Process 3346 attached
read(4,

クライアントで"12345"を入力します。

[root@server ~]# ./cl
12345

クライアントで入力した文字列("12345")が、サーバのFD=4より読み出されていることがわかります。

[root@server ~]#  strace -f -e trace=read,write -e read=3,4 -e write=3,4 -p 3346
strace: Process 3346 attached
read(4, "12345\n", 100)                 = 6
 | 00000  31 32 33 34 35 0a                                 12345.           |
write(1, "12345\n", 6)                  = 6
read(4,

13 統計情報を確認する方法(-S)

13.1 システムコールの実行時間でソートする方法(time)

lsコマンドが実行するシステムコールを実行時間の多い順に出力してみます。 mmapシステムコールの実行時間の割合(22.66%)が一番多いことがわかります。

[root@server ~]# strace -c -S time ls
Anaconda3-2021.05-Linux-x86_64.sh    a.txt            anaconda3  c.txt   stress.1TGpB0  stress.BKIcbc  sv.dat    tp
Anaconda3-2021.05-Linux-x86_64.sh.1  anaconda-ks.cfg  b.txt      python  stress.8BbTyX  stress.vgARxb  test.txt  tp.c
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 22.66    0.000213           7        27           mmap
 21.81    0.000205          11        18           mprotect
 10.96    0.000103          10        10           open
  7.98    0.000075           9         8           read
  5.32    0.000050           3        13           close
  5.00    0.000047           4        11           fstat
  4.79    0.000045          22         2           statfs
  4.04    0.000038          19         2           munmap
  3.40    0.000032          16         2           write
  3.19    0.000030          15         2           getdents
  2.02    0.000019           9         2           ioctl
  1.81    0.000017          17         1           openat
  1.60    0.000015          15         1           stat
  1.17    0.000011           3         3           brk
  1.17    0.000011           5         2         1 access
  0.85    0.000008           4         2           rt_sigaction
  0.53    0.000005           5         1           getrlimit
  0.43    0.000004           4         1           rt_sigprocmask
  0.43    0.000004           4         1           arch_prctl
  0.43    0.000004           4         1           set_tid_address
  0.43    0.000004           4         1           set_robust_list
  0.00    0.000000           0         1           execve
------ ----------- ----------- --------- --------- ----------------
100.00    0.000940                   112         1 total

13.2 システムコールの呼び出し回数でソートする方法(calls)

lsコマンドが実行するシステムコールを呼び出し回数の多い順に出力してみます。 mmapシステムコールの呼び出し回数(27回)が一番多いことがわかります。 また、openシステムコールは実行時間が占める割合(14.74%)は3番目ですが、呼び出し回数(10回)としては5番目であることがわかります。

[root@server ~]# strace -c -S calls ls
Anaconda3-2021.05-Linux-x86_64.sh    a.txt            anaconda3  c.txt   stress.1TGpB0  stress.BKIcbc  sv.dat    tp
Anaconda3-2021.05-Linux-x86_64.sh.1  anaconda-ks.cfg  b.txt      python  stress.8BbTyX  stress.vgARxb  test.txt  tp.c
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 24.79    0.000301          11        27           mmap
 19.69    0.000239          13        18           mprotect
  5.44    0.000066           5        13           close
  5.02    0.000061           5        11           fstat
 14.74    0.000179          17        10           open
  5.02    0.000061           7         8           read
  1.40    0.000017           5         3           brk
  2.47    0.000030          15         2           write
  3.21    0.000039          19         2           munmap
  0.74    0.000009           4         2           rt_sigaction
  1.24    0.000015           7         2           ioctl
  4.94    0.000060          30         2         1 access
  2.47    0.000030          15         2           getdents
  3.71    0.000045          22         2           statfs
  1.15    0.000014          14         1           stat
  0.33    0.000004           4         1           rt_sigprocmask
  0.74    0.000009           9         1           execve
  0.41    0.000005           5         1           getrlimit
  0.33    0.000004           4         1           arch_prctl
  0.41    0.000005           5         1           set_tid_address
  1.40    0.000017          17         1           openat
  0.33    0.000004           4         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.001214                   112         1 total

13.3 システムコールの名前でソートする方法(name)

システムコールの名前をアルファベット順でソートしてみます。 access,arch_prctl,brkの順で表示されていることがわかります。

[root@server ~]# strace -c -S name ls
Anaconda3-2021.05-Linux-x86_64.sh    a.txt            anaconda3  c.txt   stress.1TGpB0  stress.BKIcbc  sv.dat    tp
Anaconda3-2021.05-Linux-x86_64.sh.1  anaconda-ks.cfg  b.txt      python  stress.8BbTyX  stress.vgARxb  test.txt  tp.c
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  1.52    0.000011           5         2         1 access
  0.55    0.000004           4         1           arch_prctl
  1.52    0.000011           3         3           brk
  5.95    0.000043           3        13           close
  0.00    0.000000           0         1           execve
  4.15    0.000030           2        11           fstat
  4.15    0.000030          15         2           getdents
  0.69    0.000005           5         1           getrlimit
  3.18    0.000023          11         2           ioctl
 21.72    0.000157           5        27           mmap
 21.44    0.000155           8        18           mprotect
  5.12    0.000037          18         2           munmap
  9.27    0.000067           6        10           open
  2.07    0.000015          15         1           openat
  3.60    0.000026           3         8           read
  1.24    0.000009           4         2           rt_sigaction
  0.55    0.000004           4         1           rt_sigprocmask
  0.41    0.000003           3         1           set_robust_list
  0.55    0.000004           4         1           set_tid_address
  1.94    0.000014          14         1           stat
  5.95    0.000043          21         2           statfs
  4.43    0.000032          16         2           write
------ ----------- ----------- --------- --------- ----------------
100.00    0.000723                   112         1 total

Z 参考情報

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

tcpdumpの使い方(基本編)

1 tcpdumpとは?

パケットをキャプチャするためのコマンドです。

以下の記事も作成しました。 tcpdumpの使い方(パケットファイルの切り替え方法) - hana_shinのLinux技術ブログ

2 検証環境

2.1 ネットワーク構成

サーバとクライアントの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 インストール方法

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

tcpdumpの版数を確認します。版数は4.9.2です。

[root@server ~]# tcpdump -h
tcpdump version 4.9.2
-snip-

4 オプション一覧

[root@server ~]# tcpdump -h
tcpdump version 4.9.2
libpcap version 1.5.3
OpenSSL 1.0.2k-fips  26 Jan 2017
Usage: tcpdump [-aAbdDefhHIJKlLnNOpqStuUvxX#] [ -B size ] [ -c count ]
                [ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]
                [ -i interface ] [ -j tstamptype ] [ -M secret ] [ --number ]
                [ -Q|-P in|out|inout ]
                [ -r file ] [ -s snaplen ] [ --time-stamp-precision precision ]
                [ --immediate-mode ] [ -T type ] [ --version ] [ -V file ]
                [ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ]
                [ -Z user ] [ expression ]

5 パケットキャプチャに使用できるインタフェースを調べる方法(-D)

tcpdumpを実行するさい、パケットをキャプチャするインタフェースを指定します。 -Dオプションを指定すると、キャプチャに使用できるインタフェース一覧を表示することができます。 本記事では、インタフェースとしてens33を使います。

[root@server ~]# tcpdump -D
1.virbr0
2.bluetooth0 (Bluetooth adapter number 0)
3.nflog (Linux netfilter log (NFLOG) interface)
4.nfqueue (Linux netfilter queue (NFQUEUE) interface)
5.usbmon1 (USB bus number 1)
6.usbmon2 (USB bus number 2)
7.ens33
8.any (Pseudo-device that captures on all interfaces)
9.lo [Loopback]

6 プロミスキャス・モードへの移行を抑止する方法(-p)

tcpdumpを実行すると、tcpdumpNICをプロミスキャス・モードにします。 プロミスキャス・モードは、自分宛だけではなく、ネットワーク上を流れるすべてのパケットを取り込むという動作になります。 自分宛以外のパケットを取り込むというのは、Ethernetフレームの宛先MACアドレスが、自身のNICMACアドレスとは異なっていても、Ethernetフレームを取り込むことを意味しています。

-pを指定することで、NICがプロミスキャス・モードになるのを抑止します。

[root@server ~]# tcpdump -p -i ens33 src host 192.168.2.105

なお、-pオプションを指定しないと、/var/log/messagesに下記メッセージが記録されます。

Dec 25 19:03:49 server kernel: device ens33 entered promiscuous mode

また、tcpdumpを終了すると、/var/log/messagesに下記メッセージが記録されます。

Dec 25 19:06:04 server kernel: device ens33 left promiscuous mode

6 送信元IPアドレス/宛先IPアドレスを指定する方法(src ip/dst ip)

ここでは、送信元IPアドレスが、192.168.2.105(クライアント)のパケットをキャプチャしてみます。

[root@server ~]# tcpdump -i ens33 src host 192.168.2.105

次に、クライアントでサーバ宛てのpingを実行します。 このとき、クライアントでping -c 1 192.168.2.100と入力すると、サーバ側の画面にキャプチャしたパケットが 表示されるのがわかります。これは、先に説明したNICがプロミスキャス・モードになっているからです。 プロミスキャス・モードだと、関係のないパケットを受信して解析がやりにくいので、以下のように-pを指定してサーバの NICをプロミスキャス・モードになるのを抑止します。

[root@server ~]# tcpdump -p -i ens33 src host 192.168.2.105

次にクライアントでpingを1回だけ実行します。 なお、pingの使い方は、pingコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# ping -c 1 192.168.2.100

サーバ側のtcpdump実行結果を確認します。 送信元IPが192.168.2.105のパケットが表示されていることがわかります。

[root@server ~]# tcpdump -p -i ens33 src host 192.168.2.105
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
17:48:36.675851 IP 192.168.2.105 > server: ICMP echo request, id 2725, seq 1, length 64

7 送信元ネットワークアドレス/宛先ネットワークアドレスを指定方法(src net/dst net)

送信元ネットワークアドレスを指定する場合は、以下のように指定します。

[root@server ~]# tcpdump -p -i ens33 src net 192.168.3.0/24
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
-snip-

8 送信元ポート番号/宛先ポート番号を指定する方法(src port/dst port)

8.1 事前準備

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

[root@server ~]# nc -kl 11111

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

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

TCPの11111番ポートを開放します。

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

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

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

8.2 実行結果

ここでは、宛先ポート番号が11111のパケットをキャプチャしてみます。

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

クライアントでncコマンドを実行します。 宛先IPアドレスにサーバのIP、ポート番号に11111番ポートを指定します。

[root@client ~]# nc 192.168.2.100 11111

tcpdumpの実行結果を確認します。 クライアントからTCPの11111番ポートへのパケットが出力されていることがわかります。

[root@server ~]# tcpdump -p -i ens33 dst 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
19:57:33.430921 IP 192.168.2.105.60632 > 192.168.2.100.11111: Flags [S], seq 3811849999, win 29200, options [mss 1460,sackOK,TS val 13081167 ecr 0,nop,wscale 7], length 0
19:57:33.431708 IP 192.168.2.105.60632 > 192.168.2.100.11111: Flags [.], ack 2737242265, win 229, options [nop,nop,TS val 13081182 ecr 13213087], length 0

なお、上記tcpdumpの実行結果は、以下の①、③のパケット(宛先ポート番号が11111)がキャプチャされています。

client                                          server
  |                                               |
  |                                               |
  |-------------------- SYN --------------------->| ①
  |                                               |
  |<------------------- SYN + ACK --------------->| ②
  |                                               |
  |-------------------- ACK --------------------->| ③
  |                                               |

9 ARPパケットをキャプチャする方法(arp)

ARPパケットをキャプチャするため、以下のように実行します。

[root@server ~]# tcpdump -p -i ens33 arp

次に、クライアントでarpingコマンドを実行します。 arpingコマンドを実行すると、クライアントからサーバにARPリクエストパケットが1つ送信されます。 ARPリクエストパケットを送信すると、サーバのIPアドレスからサーバのNICMACアドレスを求めることができます。 以下の実行結果より、サーバのNIC(192.168.2.100)のMACアドレスはxx:xx:xx:yy:yy:yyであることがわかります。

[root@client ~]# arping -c 1 -I ens33 192.168.2.100
ARPING 192.168.2.100 from 192.168.2.105 ens33
Unicast reply from 192.168.2.100 [xx:xx:xx:yy:yy:yy]  1.674ms
Sent 1 probes (1 broadcast(s))
Received 1 response(s)

tcpdumpの実行結果は以下のようになります。 ARPリクエストを受信したあと、ARPリプライを返信していることがわかります。 またARPリプライで、サーバのNIC(192.168.2.105)のMACアドレスがxx:xx:xx:yy:yy:yyであることを返信しています。

[root@server ~]# tcpdump -p -i ens33 arp
20:56:00.575290 ARP, Request who-has server (Broadcast) tell 192.168.2.105, length 46
20:56:00.575315 ARP, Reply server is-at xx:xx:xx:yy:yy:yy (oui Unknown), length 28

10 ICMPパケットをキャプチャする方法(icmp)

ICMPパケットをキャプチャするため、以下のように実行します。

[root@server ~]# tcpdump -p -i ens33 icmp

次に、クライアントでpingコマンドを実行します。pingの宛先はサーバを指定します。

[root@client ~]# ping -c 1 192.168.2.100

サーバでtcpdumpの実行結果を確認します。 ICMP echo requestを受信して、その応答としてICMP echo replyをクライアントに返信していることがわかります。

[root@server ~]# tcpdump -p -i ens33 icmp
21:22:45.156777 IP 192.168.2.105 > server: ICMP echo request, id 4741, seq 1, length 64
21:22:45.156856 IP server > 192.168.2.105: ICMP echo reply, id 4741, seq 1, length 64

11 IPv6パケットをキャプチャする方法(ip6)

IPv6パケットをキャプチャするため、以下のように実行します。

[root@server ~]# tcpdump -p -i ens33 ip6

次に、クライアントでpingコマンドを実行します。pingの宛先はサーバを指定します。 pingの宛先はIPv6のリンクローカルアドレスを指定します。 なお、pingの使い方は、pingコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# ping -6 -c 1 -I ens33 fe80::174:936a:8876:8055

サーバでtcpdumpの実行結果を確認します。 ICMP echo requestを受信して、その応答としてICMP echo replyをクライアントに返信していることがわかります。

[root@server ~]# tcpdump -p -i ens33 ip6
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
21:31:29.365316 IP6 fe80::3e2:a9c0:9fa6:a8ad > server: ICMP6, echo request, seq 1, length 64
21:31:29.365391 IP6 server > fe80::3e2:a9c0:9fa6:a8ad: ICMP6, echo reply, seq 1, length 64

12 簡潔な出力をする方法(-q)

-qオプションを指定すると、出力が簡潔になります。 TCPの場合、TCPヘッダの一部の情報だけを表示します。

[root@server ~]# tcpdump -p -i ens33 dst port 11111 -n -q
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
19:08:07.494545 IP 192.168.2.105.43018 > 192.168.2.100.vce: tcp 2

13 時刻に関するパラメータの使い方

13.1 直前にキャプチャした時刻からの経過時刻表示(-ttt)

tcpdumpを実行します。

[root@server ~]# tcpdump -ttt -i ens33 icmp

クライアントからサーバにpingを実行します。

[root@client ~]# ping -c 3 192.168.2.100

tcpdumpの実行結果を確認します。 ICMP echo replyを送信してから、1秒に ICMP echo requestを受信していることがわかります。

[root@server ~]# tcpdump -ttt -i ens33 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
 00:00:00.000000 IP 192.168.2.105 > server: ICMP echo request, id 1814, seq 1, length 64
 00:00:00.000113 IP server > 192.168.2.105: ICMP echo reply, id 1814, seq 1, length 64
 00:00:01.001217 IP 192.168.2.105 > server: ICMP echo request, id 1814, seq 2, length 64
 00:00:00.000084 IP server > 192.168.2.105: ICMP echo reply, id 1814, seq 2, length 64
 00:00:01.003023 IP 192.168.2.105 > server: ICMP echo request, id 1814, seq 3, length 64
 00:00:00.000080 IP server > 192.168.2.105: ICMP echo reply, id 1814, seq 3, length 64

13.2 時刻を西暦、時分秒で表示する表示する(-tttt)

tcpdumpを実行します。

[root@server ~]# tcpdump -tttt -i ens33 icmp

クライアントからサーバにpingを実行します。

[root@client ~]# ping -c 3 192.168.2.100

tcpdumpの実行結果を確認します。時刻が西暦、時分秒で表示されていることがわかります。

[root@server ~]# tcpdump -tttt -i ens33 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
2021-12-26 12:27:51.509024 IP 192.168.2.105 > server: ICMP echo request, id 1839, seq 1, length 64
2021-12-26 12:27:51.509150 IP server > 192.168.2.105: ICMP echo reply, id 1839, seq 1, length 64
2021-12-26 12:27:52.515449 IP 192.168.2.105 > server: ICMP echo request, id 1839, seq 2, length 64
2021-12-26 12:27:52.515535 IP server > 192.168.2.105: ICMP echo reply, id 1839, seq 2, length 64
2021-12-26 12:27:53.531047 IP 192.168.2.105 > server: ICMP echo request, id 1839, seq 3, length 64
2021-12-26 12:27:53.531129 IP server > 192.168.2.105: ICMP echo reply, id 1839, seq 3, length 64

13.3 最初にキャプチャしたパケットからの相対時刻を表示する方法(-ttttt)

tcpdumpを実行します。

[root@server ~]# tcpdump -ttttt -i ens33 icmp

クライアントからサーバにpingを実行します。

[root@client ~]# ping -c 3 192.168.2.100

tcpdumpの実行結果を確認します。 最初にキャプチャしたパケットから1秒後、2秒後の時刻が表示されていることがわかります。

[root@server ~]# tcpdump -ttttt -i ens33 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
 00:00:00.000000 IP 192.168.2.105 > server: ICMP echo request, id 1889, seq 1, length 64
 00:00:00.000221 IP server > 192.168.2.105: ICMP echo reply, id 1889, seq 1, length 64
 00:00:01.003282 IP 192.168.2.105 > server: ICMP echo request, id 1889, seq 2, length 64
 00:00:01.003370 IP server > 192.168.2.105: ICMP echo reply, id 1889, seq 2, length 64
 00:00:02.004585 IP 192.168.2.105 > server: ICMP echo request, id 1889, seq 3, length 64
 00:00:02.004670 IP server > 192.168.2.105: ICMP echo reply, id 1889, seq 3, length 64

14 ホスト名やサービス名を数値に変更する方法

14. 1 事前準備

サーバでhostsファイルを編集します。 サーバとクライアントのホスト名とIPアドレスを登録します。

[root@server ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.2.100 server
192.168.2.105 client

テスト用のポート番号を開放します。

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

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

[root@server ~]# nc -kl 11111

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

まず-nオプションを使わない場合について確認してみます。

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

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

[root@client ~]# nc 192.168.2.100 11111

tcpdumpの実行結果を確認します。IPアドレスではなくホスト名が表示されていることがわかります。

[root@server ~]# tcpdump -p -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
17:59:30.015908 IP client.43014 > server.vce: Flags [S], seq 2375339819, win 29200, options [mss 1460,sackOK,TS val 705644 ecr 0,nop,wscale 7], length 0
17:59:30.015996 IP server.vce > client.43014: Flags [S.], seq 2426277720, ack 2375339820, win 28960, options [mss 1460,sackOK,TS val 785656 ecr 705644,nop,wscale 7], length 0
17:59:30.016493 IP client.43014 > server.vce: Flags [.], ack 1, win 229, options [nop,nop,TS val 705644 ecr 785656], length 0

なお上記のvceはサービス名です。サービス名は/etc/servicesファイルで定義されています。 /etc/servicesでサービス名とポート番号の対応関係が定義されています。

[root@server ~]# cat /etc/services |grep 11111
vce             11111/tcp               # Viral Computing Environment (VCE)
vce             11111/udp               # Viral Computing Environment (VCE)

次に、-nオプションを指定してみます。

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

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

[root@client ~]# nc 192.168.2.100 11111
12345

今度は、ホスト名ではなくIPアドレスが表示されたことがわかります。

[root@server ~]# tcpdump -p -i ens33 port 11111 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
18:01:29.037076 IP 192.168.2.105.43014 > 192.168.2.100.vce: Flags [P.], seq 2375339820:2375339826, ack 2426277721, win 229, options [nop,nop,TS val 824666 ecr 785656], length 6
18:01:29.037146 IP 192.168.2.100.vce > 192.168.2.105.43014: Flags [.], ack 6, win 227, options [nop,nop,TS val 904677 ecr 824666], length 0

14. 3 ホスト名とサービス名を数値で表示する方法(-nn)

次は、-nnオプションを指定して、サービス名をポート番号で表示してみます。

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

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

[root@client ~]# nc 192.168.2.100 11111
12345
11111

tcpdumpの実行結果を確認します。サービス名も数値で表示されていることがわかります。

[root@server ~]# tcpdump -p -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
18:06:51.357431 IP 192.168.2.105.43014 > 192.168.2.100.11111: Flags [P.], seq 2375339826:2375339832, ack 2426277721, win 229, options [nop,nop,TS val 1146986 ecr 904677], length 6
18:06:51.357486 IP 192.168.2.100.11111 > 192.168.2.105.43014: Flags [.], ack 6, win 227, options [nop,nop,TS val 1226997 ecr 1146986], length 0

15 パケットの中身を表示する方法

15.1 パケットの中身を16進数で表示する方法(-x)

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

[root@server ~]# nc -kl 11111

tcpdumpを実行します。このとき、-xオプションを指定してパケットの中身を16進表示してみます。

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

クライアントでncコマンドを実行します。 TCPコネクションを確立した後、クライアントからサーバに文字列(12345)を送信します。

[root@client ~]# nc 192.168.2.100 11111
12345

tcpdumpの実行結果を確認します。 パケット末尾のほうに、3132 3334 35と表示されているのがわかります。

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

18:53:24.056781 IP 192.168.2.105.43018 > 192.168.2.100.11111: Flags [P.], seq 976391533:976391539, ack 1612350400, win 229, options [nop,nop,TS val 3939685 ecr 3745633], length 6
        0x0000:  4500 003a 8dcc 4000 4006 26d4 c0a8 0269
        0x0010:  c0a8 0264 a80a 2b67 3a32 8d6d 601a 83c0
        0x0020:  8018 00e5 9214 0000 0101 080a 003c 1d65
        0x0030:  0039 2761 3132 3334 350a

15.2 パケットの中身を16進数とASCII文字で表示する方法(-X)

15.1と同じ手順で実行します。 このとき、tcpdumpのオプションには -Xを指定します。 -Xを指定を指定することで、パケットの中身を16進数とASCII文字で表示することができます。 以下に実行結果を示します。

[root@server ~]# tcpdump -p -i ens33 dst port 11111 -nn -X
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
19:05:30.989783 IP 192.168.2.105.43018 > 192.168.2.100.11111: Flags [P.], seq 976391539:976391545, ack 1612350400, win 229, options [nop,nop,TS val 4666618 ecr 4019697], length 6
        0x0000:  4500 003a 8dcd 4000 4006 26d3 c0a8 0269  E..:..@.@.&....i
        0x0010:  c0a8 0264 a80a 2b67 3a32 8d73 601a 83c0  ...d..+g:2.s`...
        0x0020:  8018 00e5 4bda 0000 0101 080a 0047 34fa  ....K........G4.
        0x0030:  003d 55f1 3132 3334 350a                 .=U.12345.

Z 参考情報

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