WiresharkではじめるTCP超基礎入門


Webプログラマはネットワークの低レイヤーを意識しないで開発が行えるため、TCPの知識なしで仕事が可能です。
しかし、知識のあるなしでインフラ含むシステム全体に関する理解度が変わります。
そこで、以前若手エンジニア向けに行ったちょっとした講座内容を整理して公開します。
オリジナルはGithubに上がってますが、口頭で解説することを前提としてる雑な書き方なので無視してください)
内容は本当に簡単で薄いので、あまり期待しないでください。

Wiresharkを使ってHTTP通信を解析する

Wiresharkを使ってHTTP通信を覗き見ることで、TCPの動きを体験してもらいます。
また、環境はMacを前提としています。
WindowsやLinuxでも同様のことは出来ますが、手順が異なるので注意してください。

1. Wiresharkとは

パケットキャプチャして解析できるGUIツールです。
Etherealと呼ばれていたツールが元になっています。

2. Wiresharkインストール

上記からダウンロードもしくはbrewでインストールします。

$ brew cask install wireshark

起動してキャプチャができない的なメッセージが出た場合は ChmodBPFプラグインをインストールします。
Wiresharkのバグのようです。
GUIの場合は画面にメッセージが表示されるので従ってください。
brewでインストールした場合は同様にbrewでインストールしてください。

$ brew cask install wireshark-chmodbpf

3. Local環境にWebサーバを立ち上げる

Dockerで立ち上げます。

dockerfile
FROM nginx:latest
docker-compose.yml
version: "3"
services:
  nginx:
    build: .
    ports:
      - "8080:80"
$ docker-compose up --build

4. Wiresharkでパケットキャプチャする

Wiresharkを起動させるとNICの選択が出てきます。
NICリストが空の場合はWiresharkインストールで書いてあるとおりChmodBPプラグインをインストールしてください。

さて、Dockerで立ち上げたWebサーバと通信しているインターフェイスを探します。

$ ifconfig

Dockerは docker0 インターフェイスを介して通信を行いますが、リストに表示されないと思います。
なぜならMacにはセキュリティ的な制約があり docker0 ブリッジネットワークは存在しません。

なので、Wiresharkでキャプチャすべきインターフェイスは lo(Loopback) となります。

5. Web通信の絞り込み

それでは、Webサーバと通信します。

ブラウザだと http://localhost:8080/ curlなら $ curl localhost:8080 のように入力してください。

初期に画面を開いた状態では、 lo と通信する全てのパケットが表示されているため、どれがWebサーバと通信しているパケットなのか分かりません。
そこで情報を絞り込みます。
クエリ入力バーに下記を入力してみてください。

tcp.port == 8080

Webサーバは 8080 ポートで起動しているため、このポート宛の通信のみに情報を絞り込んだ状態です。

6. tcpの超基礎

通信内容を見る前に、tcpの超基礎を知っておく必要があります。
tcpはコネクション型プロトコルです。
ちなみにtcpは Transmission Control Protocol の略です。
そのため本体のデータ(今回の場合はHTTPデータ)を転送する前にコネクションを確立する必要があります。
このコネクションの確立は3way handshakeで行われます。
ざっくりASCIIで描くと下記な感じです。

Client              Server
  |                   |
  | ---- SYN -------> |  1: 通信始めるよ
  |                   |
  | <--- SYN, ACK --- |  2: 通信開始了解です
  |                   |
  | ---- ACK -------> |  3: 通信開始了解を受け取ったからデータ送るよ
  |                   |

コネクション終了時は下記の感じです。

Client              Server
  |                   |
  | ---- FIN, ACK --> |  1: 通信終了するよ
  |                   |
  | <--- ACK -------- |  2: 通信終了了解です
  | <--- FIN, ACK --- |  3: 通信終了するよ
  |                   |
  | ---- ACK -------> |  4: 通信終了了解です
  |                   |

SYN/ACK/FIN: 通信の意味を定義するコードビットと呼ばれるフラグ

7. 通信内容を見てみる

通信データ時系列順にリスト表示されている項目を見ると、コネクション関連のSYNやらFINやらが先頭と最後に表示されているのがわかります。
肝心のHTTPデータ通信はそれらに挟まれた真ん中に存在します。

Wiresharkはよくあるプロトコル(HTTPとか)は自動的に解析して表示してくれます。
通信データリストの Info 項目を見ると、 GET / HTTP/1.1HTTP/1.1 200 OK といった表示があります。
これがHTTP通信したデータです。

リストを選択するとデータの中身が下のwindowに表示されます。
バイナリデータの右側にASCII文字列に変換されたものが表示されていると思います。
その内容はブラウザ(もしくはcurl)のリクエストデータとレスポンスデータと同じものが入っているはずです。
表示されていない人は View -> Packet Bytes チェックをONにしてください。

また、WiresharkがHTTPを解析して構造分けした内容が表示されているwindowもあります。
表示されてない人は View -> Packet Details チェックをONにしてください。
このwindowの Hypertext Transfer Protocol を開くと、各種ヘッダー情報などが解析されて個別に見れるようになっています。
また、それらのデータがパケットのバイナリデータのどこに該当するかもハイライト表示されていると思います。

TCPのヘッダーフォーマット

TCP通信でやり取りするデータ構造(TCPヘッダー)を見ていきます。
TCPヘッダーをASCIIで表現するの難しいので下記サイトなどを参照してください。

Wiresharkでパケットキャプチャした実際のデータは下記になります。
HTTPでGETリクエストを送った箇所です。

0000   02 00 00 00 45 00 00 82 00 00 40 00 40 06 00 00   ....E.....@.@...
0010   7f 00 00 01 7f 00 00 01 cb 74 1f 90 a0 e9 a6 44   .........t.....D
0020   4c 34 73 0d 80 18 18 eb fe 76 00 00 01 01 08 0a   L4s......v......
0030   13 f4 ed bd 13 f4 ed bd 47 45 54 20 2f 20 48 54   ........GET / HT
0040   54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 6c 6f   TP/1.1..Host: lo
0050   63 61 6c 68 6f 73 74 3a 38 30 38 30 0d 0a 55 73   calhost:8080..Us
0060   65 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c 2f 37   er-Agent: curl/7
0070   2e 35 34 2e 30 0d 0a 41 63 63 65 70 74 3a 20 2a   .54.0..Accept: *
0080   2f 2a 0d 0a 0d 0a                                 /*....

このデータを分解していきます。

02 00 00 00 : イーサネットフレームのタイプ(説明略)

イーサネットフレームはOSI参照モデルもしくはTCP/IP階層モデルのLayer2の領域になります。
オンプレ環境を構築したりk8sのパケットを追う(VXLAN)場合はこの領域の知識が必要になりますが、説明は省略します。

45 00 00 82 00 00 40 00 40 06 00 00 7f 00 00 01 7f 00 00 01 : IPヘッダー(説明略)

送信元IPとかが入ってます。
ここも説明は省略します。

以下がTCPヘッダーになります。

cb 74 1f 90 a0 e9 a6 44 4c 34 73 0d 80 18 18 eb fe 76 00 00 01 01 08 0a 13 f4 ed bd 13 f4 ed bd : TCPヘッダー

これをさらに細かく見ていきます。

cb 74 : 送信元ポート(52084)
1f 90 : 送信先ポート(8080)
a0 e9 a6 44 : シーケンス番号
4c 34 73 0d : 確認応答番号
80 : ヘッダー長(厳密には 8
80 18 : フラグ(厳密には 808 を除く)
フラグを分解すると 000 0 0 0 0 1 1 0 0 0 となる。 AcknowledgmentPush フラグがONになっている
18 eb : ウィンドウサイズ
fe 76 : チェックサム
00 00 : 緊急ポインタ
01 01 08 0a 13 f4 ed bd 13 f4 ed bd : オプション

Wiresharkの構造解析ウィンドウにはこれらを自動的に解析した結果が表示されますので確認してみてください。
アプリケーションエンジニア的にTCPヘッダーで特筆すべきは ウィンドウサイズ になります。
インターネット爆速!みたいに謳っているソフトウェアが昔ありましたが(今あるかは知らない)、このウィンドウサイズを調整することでパフォーマンスを上げることが可能です。
また、TCPヘッダー以降がデータグラムになります。データグラムの中身がHTTPでやりとりするデータとなります。

まとめ

どうでしたでしょうか。
座学もいいですが、実際の通信を可視化することでより理解が深まるかと思います。
この記事が誰かの助けになれば幸いです。