
コマンドライン上でリアルタイムにプロトコル内容を確認できる tshark のススメ

はじめまして。新人の H.M.です。
サーバ上の通信を確認するのに、tcpdump はよく使われていると思います。プロトコルが絡んでちょっと詳しく調べたいというときには、tcpdump で保存したパケットキャプチャのデータを PC に持ってきて Wireshark で確認しているという方もそれなりにいらっしゃるでしょう。
Linux でもデスクトップがあれば Wireshark で GUI を使ってキャプチャと分析ができますが、デスクトップを入れていないサーバではいちいちキャプチャをダンプして PC に持ってくる作業が面倒ですよね。
実は Linux 向けの Wireshark には tshark というコマンドラインツールも付いています。tshark は tcpdump のようにリアルタイムにパケットをキャプチャをするだけでなく、プロトコルを解釈した内容も表示することができます。更に、Wireshark のフィルタも使用することができ、通信量が多い本番環境でも確認したい通信内容をアプリケーション層レベルで絞り込んで確認することができます。
インストールと動作確認、キャプチャフィルタ
tshark は RedHat系 OS であれば wireshark パッケージに含まれていますので、下記のようにインストールします。
# dnf install wireshark
パケットのキャプチャ時は、tcpdump と同様にインタフェースを -i オプションで指定します。当然ですがキャプチャをするには root 権限が必要です。-i オプションは複数指定でき、フォワーディングしている場合などでも確認に便利です。
# tshark -i eth0
これで tcpdump のようにキャプチャしたパケットが流れているのを確認できたと思います。
tshark のキャプチャ時のデフォルトのフィルタはキャプチャフィルタと呼ばれています。tshark はオプション指定以外の文字列がデフォルトのフィルタコマンドになります。オプションとデフォルトフィルタの順序は任意なのですが、入り乱れると混乱しやすいので、デフォルトのフィルタは常にコマンドラインの最後に記述することをお勧めします。
Wireshark / tshark は tcpdump と同じく pcap ライブラリでパケットのキャプチャをしており、キャプチャフィルタは pcap ライブラリと同じ構文(つまり tcpdump のフィルタと同じ構文)になります。
例)HTTP パケットを確認する
# tshark -i eth0 port 80
例)SSH 以外の TCP パケットを確認する
# tshark -i eth0 'tcp and not ( port 22 )'
「(」,「)」はシェルが解釈してしまうので、キャプチャフィルタに指定する場合にはシングルクォートもしくはダブルクォートで囲む必要があります。
tshark ではキャプチャしたパケットのプロトコルを解釈して、その内容も出力してくれます。例えば HTTP の場合、下記のような出力が確認できます。
4 0.009528648 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy HTTP 421 GET / HTTP/1.1
:
6 0.009723876 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx HTTP 552 HTTP/1.1 302 Found (text/html)
ファイルへの保存と読み込み、リードフィルタ
キャプチャしたパケットデータを -w オプションでファイルに保存できます。Ctrl-C キー等で tshark を終了させるまでの間の、キャプチャフィルタ(下記の例では「port 80」)適用後のパケットがファイルに保存されます。
# tshark -i eth0 -w /tmp/http.cap port 80
-w オプションと共に -P オプションを付けると、キャプチャして保存するパケットの状況が画面にも表示されます。
# tshark -i eth0 -w /tmp/http.cap -P port 80
キャプチャを保存したファイルは -r オプションで読み込ませることで、後から tshark で通信内容を確認できます。もちろん Wireshark があれば、そちらに読み込ませて解析させることもできます。
# tshark -r /tmp/http.cap
上記の例では、キャプチャ時に保存されたパケット情報の全てが表示されます。
キャプチャしたデータをファイルから読み込む場合(つまり -r オプションを指定した場合)にもフィルタを指定できます。読み込み時のデフォルトのフィルタはリードフィルタと呼ばれ、Wireshark のフィルタ構文を使用します。キャプチャフィルタと同様、リードフィルタがデフォルトフィルタの場合もコマンドラインの最後に記述するのが無難です。
ここで、リードフィルタに http を指定してみます。
# tshark -r /tmp/http.cap http
すると、http プロトコルの内容に絞って結果が表示されます。
4 0.000894571 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy HTTP 145 GET / HTTP/1.1
6 0.002159882 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx HTTP 518 HTTP/1.1 301 Redirect (text/html)
下記のようにすれば、HTTP リクエストの Host ヘッダが特定のドメイン(ここでは「www.xxx.co.jp」)であるトラフィックについて絞り込めます。
# tshark -r /tmp/http.cap http.host == www.xxx.co.jp
更に、URI のパス(ここでは「/category」から始まる場合)のマッチでフィルタすることができます。
# tshark -r /tmp/http.cap 'http.host == www.xxx.co.jp and http.request.uri ~ "^/category"'
なお、上記ではチルダ(~)をシェルが解釈してしまうため、リードフィルタ全体をシングルクォートでまとめています。
もう一つの例として、メール送信を確認してみましょう。tshark でポート 25 の通信をキャプチャして保存します。
# tshark -i eth0 -w /tmp/smtp.cap port 25
別のターミナルでメールを送信してみます。下記の例では s-nail を使ってメール送信をしています。(サーバからの送信に OP25B 制限はなく、認証も不要な前提です。送信先の SMTP サーバは調べてください。SPF なども適宜設定しておいてくださいね)。
$ echo 'My mail body' | s-nail -s 'My mail subject' -r foo@xxx.ne.jp -S mta=smtp://mail.yyy.ne.jp:25 -S smtp-auth=none -S v15-compat=yes bar@yyy.ne.jp
キャプチャしたデータの読み込みで、リードフィルタに smtp プロトコルを指定してみます。
# tshark -r /tmp/smtp.cap smtp
下記のように、smtp プロトコルのやり取りを確認することができます。
4 0.428767210 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy SMTP nn S: 220 mail.yyy.ne.jp ESMTP unknown
6 0.428875680 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx SMTP nn C: HELO my-host-name
8 0.431094772 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy SMTP nn S: 250 mail.yyy.ne.jp
9 0.431113720 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx SMTP nn C: MAIL FROM:<foo@xxx.ne.jp>
10 0.436811277 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy SMTP nn S: 250 2.1.0 Ok
11 0.436854180 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx SMTP nn C: RCPT TO:<bar@yyy.ne.jp>
12 0.442513920 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy SMTP nn S: 250 2.1.5 Ok
13 0.442557414 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx SMTP nn C: DATA
14 0.445922827 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy SMTP nn S: 354 End data with <CR><LF>.<CR><LF>
15 0.446039398 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx SMTP nn C: DATA fragment, nn bytes
17 0.488124927 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx SMTP/IMF nn from: foo@xxx.ne.jp, subject: My mail subject, , My mail body
19 1.572386333 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy SMTP nn S: 250 2.0.0 Ok: queued as 8167842D0D6
20 1.572472159 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx SMTP nn C: QUIT
22 1.575741244 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy SMTP nn S: 221 2.0.0 Bye
アプリケーションレイヤのプロトコル情報
さて、せっかくここまでできているのであれば、アプリケーションレイヤの情報を確認したいですよね。tshark では -O オプションを付けることでアプリケーションレイヤのプロトコル情報が確認できます。
先に保存した HTTP のキャプチャ結果を見てみましょう。リードフィルタで http に絞り込んで、-O オプションで http プロトコルを指定します。
# tshark -r /tmp/http.cap -O http http
下記のような結果が返ってきます。
Frame 4: 145 bytes on wire (1160 bits), ...
Ethernet II, Src: ...
Internet Protocol Version 4, Src: ...
Transmission Control Protocol, Src Port: 40682, Dst Port: 80, Seq: 1, Ack: 1, Len: 79
Hypertext Transfer Protocol
GET / HTTP/1.1\r\n
[Expert Info (Chat/Sequence): GET / HTTP/1.1\r\n]
[GET / HTTP/1.1\r\n]
[Severity level: Chat]
[Group: Sequence]
Request Method: GET
Request URI: /
Request Version: HTTP/1.1
Host: xxx.xxx.co.jp\r\n
User-Agent: curl/7.76.1\r\n
Accept: */*\r\n
\r\n
[Full request URI: http://xxx.xxx.co.jp/]
[HTTP request 1/1]
Frame 6: 518 bytes on wire (4144 bits), ...
Ethernet II, Src: ...
Internet Protocol Version 4, Src: ...
Transmission Control Protocol, Src Port: 80, Dst Port: 40682, Seq: 1, Ack: 80, Len: 452
Hypertext Transfer Protocol
HTTP/1.1 301 Redirect\r\n
[Expert Info (Chat/Sequence): HTTP/1.1 301 Redirect\r\n]
[HTTP/1.1 301 Redirect\r\n]
[Severity level: Chat]
[Group: Sequence]
Response Version: HTTP/1.1
Status Code: 301
[Status Code Description: Moved Permanently]
Response Phrase: Redirect
Date: Mon, 07 Apr 2025 09:23:09 GMT\r\n
Connection: keep-alive\r\n
Cache-Control: no-store\r\n
Location: https://xxx.xxx.co.jp:443/\r\n
Content-Type: text/html\r\n
Content-Language: en\r\n
Accept-CH: Sec-CH-UA-Full-Version-List, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch\r\n
Permissions-Policy: ch-ua-full-version-list=*, ch-ua-model=*, ch-ua-platform-version=*, ch-ua-arch=*\r\n
Permissions-Policy: unload=()\r\n
Content-Length: 1\r\n
[Content length: 1]
\r\n
[HTTP response 1/1]
[Time since request: 0.001265311 seconds]
[Request in frame: 4]
[Request URI: http://xxx.xxx.co.jp/]
File Data: 1 bytes
Line-based text data: text/html (1 lines)
HTTP のリクエストとレスポンスのプロトコル情報、HTTPヘッダに関する情報が確認できました。
上記の例では Webページがリダイレクトしているためレスポンスボディが空になっています。もし HTTP のボディ要素があれば、ダンプ形式でその内容が表示されます。なお、HTTP のボディ要素が全てダンプされるため、一般的な Web アクセスですと出力が大量になるのでご注意ください。
キャプチャ時のアプリケーションレイヤ表示とディスプレーフィルタ
実は -O オプションはキャプチャ時にも使用できます。キャプチャ時のデフォルトフィルタはキャプチャフィルタであることに注意してください。
# tshark -i eth0 -O http port 80
こうすることで、キャプチャ時でもアプリケーションレイヤの情報を確認することができます。ただ、実行してみればわかるとおり、アプリケーションレイヤだけでなく、パケットレベルの情報も出てきて、結構うざいです。
実は、画面出力に対して Wireshark の構文でフィルタできるディスプレーフィルタというオプションがあり、これはキャプチャ時でも使用できます。ディスプレーフィルタは必ず -Y オプションで指定します。
下記の場合、キャプチャフィルタで「host yyy.yyy.yyy.yyy」を指定して通信相手の IPアドレスをフィルタし、-Y によるディスプレーフィルタで HTTP プロトコルに表示を制限して、-O で HTTP のプロトコル情報を表示させます。
# tshark -i eth0 -O http -Y http host yyy.yyy.yyy.yyy
「-Y http」を指定することで、画面には HTTP のアプリケーションレイヤのプロトコル情報だけが表示されます。
ディスプレーフィルタで Wireshark の構文による様々なフィルタの指定をおこなうことができますが、あくまで -Y オプションのパラメータなので、必要に応じて文字列をクォートすることを忘れないでください。下記は、ディスプレーフィルタで http の Host ヘッダが「xxx.xxx.co.jp」の場合に絞り込む例です。
# tshark -i eth0 -O http -Y 'http.host == xxx.xxx.co.jp' host yyy.yyy.yyy.yyy
ここまでで、tshark を使って Wireshark のフィルタ機能で表示させるトラフィックを絞り込んで、リアルタイムにアプリケーション通信の確認をおこなうことができることがご理解いただけると思います。
HTTPS通信の復号と、TCP のトレース
Webサーバで暗号化通信をしている場合、鍵の情報があればアプリケーション通信の内容を確認することができます。昔は RSA鍵交換が普通だったのでサーバ鍵を使えば共通鍵を取得して復号できたのですが、現在は一時鍵による鍵交換が主流になってきているため、気軽には HTTPS の通信を復号できません。自身で通信した時の共通鍵(Pre-Master-Secret)をファイルに保存しておくことで、その通信に限って復号できます。
パケットのキャプチャ方法は今までと同じです。HTTPS 通信なので port 443 の通信をキャプチャします。
# tshark -i eth0 -w /tmp/https.cap -P port 443
Webサーバと通信する際に、クライアント側で共通鍵をファイルに保存します。共通鍵の保存は OpenSSL ライブラリなどで対応しており、保存先ファイル名を SSLKEYLOGFILE という環境変数で指定します。下記は curl を使った例です。
# env SSLKEYLOGFILE=/tmp/sslkeylog curl -s --http1.1 --tls-max 1.2 https://www.xxx.co.jp
最近の curl は HTTP/2 や TLS 1.3 にも対応していますが、tshark はバージョンによってはサポートしていなかったり、HTTP/2 は HTTP/1.1 とはだいぶ様相が異なるなどのため、話の都合上、ここでは HTTP/1.1、TLS 1.2 以下に制限しています。
通信が終わったら tshark のキャプチャを終了させます。
curl で保存した共通鍵のファイルを「-o “tls.keylog_file:/tmp/sslkeylog”」で指定したうえで、保存したキャプチャファイルを tshark に読み込ませます。-O オプションで http のプロトコル情報を出力し、リードフィルタで http だけに出力を制限します。
# tshark -r /tmp/https.cap -o "tls.keylog_file:/tmp/sslkeylog" -O http http
下記のように、復号したアプリケーションレイヤの情報が取得できます。
Frame 12: 170 bytes on wire (1360 bits), 170 bytes captured (1360 bits) on interface eth0, id 0
Ethernet II, Src: ...
Internet Protocol Version 4, Src: ...
Transmission Control Protocol, Src Port: ...
Transport Layer Security
Hypertext Transfer Protocol
GET / HTTP/1.1\r\n
[Expert Info (Chat/Sequence): GET / HTTP/1.1\r\n]
[GET / HTTP/1.1\r\n]
[Severity level: Chat]
[Group: Sequence]
Request Method: GET
Request URI: /
Request Version: HTTP/1.1
Host: www.xxx.ne.jp\r\n
User-Agent: curl/7.76.1\r\n
Accept: */*\r\n
\r\n
[Full request URI: https://www.xxx.ne.jp/]
[HTTP request 1/1]
Frame 78: 7470 bytes on wire (59760 bits), 7470 bytes captured (59760 bits) on interface eth0, id 0
Ethernet II, Src: ...
Internet Protocol Version 4, Src: ...
Transmission Control Protocol, Src Port: ...
[2 Reassembled TCP Segments (8213 bytes): #77(1019), #78(7194)]
Transport Layer Security
Transport Layer Security
[48 Reassembled TLS segments (131897 bytes): ...
Hypertext Transfer Protocol
HTTP/1.1 200 OK\r\n
[Expert Info (Chat/Sequence): HTTP/1.1 200 OK\r\n]
[HTTP/1.1 200 OK\r\n]
[Severity level: Chat]
[Group: Sequence]
Response Version: HTTP/1.1
Status Code: 200
[Status Code Description: OK]
Response Phrase: OK
Date: Mon, 28 Apr 2025 05:19:45 GMT\r\n
Server: Apache\r\n
Link: ...
Set-Cookie: visit_count=1; expires=Tue, 29 Apr 2025 05:19:45 GMT; Max-Age=86400\r\n
Upgrade: h2,h2c\r\n
Connection: Upgrade\r\n
Strict-Transport-Security: max-age=15724800\r\n
Content-Security-Policy: upgrade-insecure-requests\r\n
Transfer-Encoding: chunked\r\n
Content-Type: text/html; charset=UTF-8\r\n
\r\n
[HTTP response 1/1]
[Time since request: 0.395051760 seconds]
[Request in frame: 12]
[Request URI: https://www.xxx.ne.jp/]
HTTP chunked response
Data chunk (8192 octets)
Chunk size: 8192 octets
Data (8192 bytes)
0000 3c 21 44 4f 43 54 59 50 45 20 68 74 6d 6c 3e 0d <!DOCTYPE html>.
0010 0a 3c 68 74 6d 6c 20 6c 61 6e 67 3d 22 6a 61 22 .<html lang="ja"
0020 3e 0d 0a 3c 68 65 61 64 20 70 72 65 66 69 78 3d >..<head prefix=
0030 22 6f 67 3a 20 68 74 74 70 3a 2f 2f 6f 67 70 2e "og: http://ogp.
0040 6d 65 2f 6e 73 23 20 66 62 3a 20 68 74 74 70 3a me/ns# fb: http:
0050 2f 2f 6f 67 70 2e 6d 65 2f 6e 73 2f 66 62 23 20 //ogp.me/ns/fb#
0060 77 65 62 73 69 74 65 3a 20 68 74 74 70 3a 2f 2f website: http://
0070 6f 67 70 2e 6d 65 2f 6e 73 2f 77 65 62 73 69 74 ogp.me/ns/websit
0080 65 23 22 3e 0d 0a 20 20 20 20 3c 6d 65 74 61 20 e#">.. <meta
0090 63 68 61 72 73 65 74 3d 22 55 54 46 2d 38 22 3e charset="UTF-8">
(以下省略)
なお、共通鍵の読み込みはキャプチャ時にも利用することができるので、同一サーバで curl などで SSLKEYLOGFILE の共通鍵ファイルを共有できるのであれば、リアルタイムに復号することができます。
# tshark -i eth0 -o "tls.keylog_file:/tmp/sslkeylog" -O http -Y http port 443
アプリケーションレイヤのトラフィックのトレース
-O オプションでは HTTP ボディ情報が HEX ダンプとテキストが並ぶかたちで表示されます。これを、「-z ‘follow,tls,ascii,0’」に置き換えることで Wireshark の TCP トレースのように通信内容を表示させることもできます。
# tshark -r /tmp/https.cap -o "tls.keylog_file:/tmp/sslkeylog" -z 'follow,tls,ascii,0' http
下記のような結果が確認できます。
12 0.025445223 xxx.xxx.xxx.xxx → yyy.yyy.yyy.yyy HTTP 170 GET / HTTP/1.1
78 0.420496983 yyy.yyy.yyy.yyy → xxx.xxx.xxx.xxx TLSv1.2 7470 [TLS segment of a reassembled PDU]HTTP/1.1 200 OK (text/html)
===================================================================
Follow: tls,ascii
Filter: tcp.stream eq 0
Node 0: ...
Node 1: :0
83
GET / HTTP/1.1
Host: www.xxx.ne.jp
User-Agent: curl/7.76.1
Accept: */*
8192
HTTP/1.1 200 OK
Date: Mon, 28 Apr 2025 05:19:45 GMT
Server: Apache
Link: ...
Set-Cookie: visit_count=1; expires=Tue, 29 Apr 2025 05:19:45 GMT; Max-Age=86400
Upgrade: h2,h2c
Connection: Upgrade
Strict-Transport-Security: max-age=15724800
Content-Security-Policy: upgrade-insecure-requests
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
2000
<!DOCTYPE html>
<html lang="ja">
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# website: http://ogp.me/ns/website#">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="format-detection" content="telephone=no">
(以下省略)
なお、-z オプションをキャプチャ時に指定することも可能ですが、キャプチャを終了してからでないとトレース結果が出力されないため、リアルタイムで確認することはできません。
特定のフィールドを出力する
-O オプションによるアプリケーションレイヤの情報は個別の通信を確認するためには便利ですが、本番などで常に通信が流れ続けている場合は大量に出力があるので不向きです。そのような場合には、プロトコル上の特定のフィールドだけを出力できると確認しやすくなります。
tshark では「-T fields」に続けて -e オプションを指定することで、プロトコル特有のフィールドも含めて特定の情報のみを出力させることができます。「-T fields」と -e オプションはキャプチャ時にも利用できます。
例えば、フレーム番号、IPアドレス、送信元ポート、送信先ポート、HTTPプロトコル、HTTP Host ヘッダ、HTTP リクエスト URL (パス) を表示させる場合、下記のようになります( -Y オプションで http パケットに絞り込んでいます)。
# tshark -i eth0 -Y http -T fields -e frame.number -e ip -e tcp.srcport -e tcp.dstport -e http -e http.host -e http.request.uri port 80
上記によって、リアルタイムのキャプチャで下記のような出力が得られます。
4 Internet Protocol Version 4, Src: xxx.xxx.xxx.xxx, Dst: yyy.yyy.yyy.yyy 45076 80 http xxx.xxx.co.jp /
6 Internet Protocol Version 4, Src: yyy.yyy.yyy.yyy, Dst: xxx.xxx.xxx.xxx 80 45076 http
更に「-e http.cookie」を追加すれば Cookie を確認することもできます。また確認した Cookie を「-Y ‘http.cookie matches “<Cookieの文字列>”‘」でディスプレーフィルターに設定すれば、その Cookie のユーザだけを追いかけることも可能になります。
まとめ
今回は、リアルタイムにパケットキャプチャしながらアプリケーションレイヤの情報も表示やフィルタ条件で使用できる tshark をご紹介しました。取りつきにくさのあるコマンドなので実例でわかりやすく解説したつもりですがいかがでしたでしょうか。更に詳しく知りたければ「man tshark」をご参照ください。
様々なアプリケーションがネットワークでつながる中、挙動のわからないアプリケーション動作をネットワーク側から確認することで解決に導けることも多々あると思いますので、皆様のお役に立てば何よりです。
それでは楽しいインターネットライフを!