TCP のフォーマット
TCP の通信データ単位は セグメント と呼ぶことが多く、ここでもそれに倣うことにします。
以下に TCP セグメントのフォーマットを示します。TCP は IPv4 の上位レイヤーとして使う場合、IPv4 のプロトコルフィールドに Ox06 (10進数:6) を指定します。
各 Field について
送信元ポート
16 bit。送信元で利用するポート番号が入ります。送信元がクライアントの場合、多くのシーンで 1024 以上の Higher Port と呼ばれるポート群から適当ものが利用されます。
宛先ポート
16 bit。宛先で利用するポート番号が入ります。宛先がサーバの場合、サービスに応じたポート番号になります。 例えば、telnet サーバへの telnet 接続であれば 23、Web サーバへの http 接続であれば 80 が入ります。
シーケンス番号/応答確認番号
共に 32 bit。どのデータを受信したかをお互い相手に知らせます。受信していないデータを検知したら再送する、といった高信頼性の提供に役立っています。また、TCP データの到着順番が変わってしまっても、データを順番通りに組み立てられるようにもなっています。
以下に 3way ハンドシェイクから始まる 一般的な TCP シーケンスにおける、Seq#/Ack# の番号の変化の例を示します。
シーケンス番号 (Seq#)は、TCP コネクションを張る際の TCP 3way ハンドシェイクの TCP syn および TCP syn/ack 送信時に、端末が各々、ランダムに初期値を決めます。(クライアント⇒サーバの方向での Seq# とサーバ⇒クライアントの方向での Seq# は異なります)
それ以降は「Seq# = 初期値 + 相手に送った TCP データのバイト数 (DataLength)」となります。例外的に、3way ハンドシェイクのパケットは実際には DataLength = 0 ですが、DataLength = 1 で計算します。
例えば、Seq#=101 で、相手に 500 bytes のデータを送ったとき、次に送る TCP セグメントの Seq# は "601" になります。(TCP データのバイト数は、DataOffset (TCP ヘッダ長) と IP ヘッダ長、IP パケット長から算出)
なお、Seq# は双方向で独立したものが必要になります。つまり、クライアント⇒サーバの方向での Seq# と、 サーバ⇒クライアントの方向での Seq# は異なります。
確認応答番号 (Ack#)は、TCP syn は必ず "0" になります。それ以降は「Ack# = 前回の相手の Seq# + DataLength」が入りますが、やはり例外的に、3way ハンドシェイクのパケットは DataLength = 1 で計算します。
なお、1 セグメント毎に確認応答していたら効率が悪いので、データの受信側では、ある一定の数だけセグメントを受け取ったら、最後の確認応答だけを返すことで、それまでの全てのセグメントを受信した、ということになります。途中で欠損があった場合は、以下の 2 パターンに分かれます。
- TCP オプションの SACK (Selective ACK) に対応している場合は、SACK によって「受信した Seq#」を返し、欠損した Seq# のデータの再送を促します。
- SACK に対応していない場合は欠損の直前までの Ack# を返し、それ以降の Seq# のデータを全て再送してもらいます。
なお、SACK に対応しているか否かは、最初の TCP 3way ハンドシェイク時のオプションで通知し合います。
データオフセット
4 bit。TCP ヘッダの長さ (4 Bytes単位) が入ります。
CWR bit、ECE bit
RFC3168 で定義された、輻輳制御のためのビットです。(鈴木さん、ご指摘ありがとうございます。)
URG bit
1 の場合、緊急通信であることを示します。使われ方としては、アプリケーション側で「緊急だ」と定義する通信において、ソケット API を利用してこの URG ビットを立て、それを受信した対向アプリケーションがどのように処理するかを決めます。TCP としては特に何も実行しません。
ただ、アプリケーション側でもこれを識別して特別な処理をさせることはほとんど無いようです。あまり意識しなくても良さそうな bit ですが、パケットキャプチャを実行して、もしこのパケットが大量に出ているなどの場合は、その通信を行っているアプリケーションベンダに問い合わせるのが良いでしょう。
ACK bit
1 の場合、応答確認番号 Field が有効であることを示します。
PSH bit
1 の場合、データ受信側はただちにデータをアプリケーションに引き渡すよう促します。これも URG bit と同じで、アプリケーション側でソケット API を利用してビットを立て、対向アプリケーションで処理を決めます。TCP としては特に何も実行しません。URG bit よりはよく使われている気はします。
例えば TeraTerm 等のターミナルソフトにおいては、コンソールで 1 文字打つたびに PSH で 1 byte のデータを送り、速やかに接続先に情報を渡す挙動になっていたりします。
RST bit
このビットが 1 にセットされた TCP セグメントを受信したホストは、ただちに TCP コネクションを切断します。
このビットは、TCP syn を受信したが、その TCP ポートが開放されていない場合や、TCP シーケンス上で正しくないと判断されるデータを受け取った場合などに不正な通信としてコネクションを切断するために利用されます。
SYN bit
TCP のスリーウェイハンドシェイクの最初の往復だけに利用されます。それ以外では利用されません。
FIN bit
送信側に送るデータが無いときに、このビットをセットして受信側に送り、(RST とは異なり、正しい通信として) コネクションを閉じる旨を通知します。ただし、コネクションは 2 方向あるので、基本的には双方から FIN bit の通信を送り合ってコネクションが完全終了となります。
ウィンドウ
16 bit。この Field は受信側が受け入れ可能なデータのバイト数を送信側に知らせるためのものです。受信側が送信側に ACK bit をセットしたセグメントを送る際にこのウィンドウ Field もセットされます。
なお、このウィンドウ Field は、効率の観点から、MSS 値の整数倍が良いとされています。あるサンプルでは、 1430 Bytes の MSS に対して、46 倍の 65780 Bytes(=約 64 KBytes) が設定されました (環境 OS:Windows7, ブラウザ:IE9)。
チェックサム
16 bit。この Field では TCP ヘッダ、TCP データだけでなく、送信元 IP アドレス、宛先 IP アドレス、プロトコル番号 (Ox06)、 TCP セグメント長も検査されます。詳細は以下のページを参考にして下さい。
緊急ポインタ
16 bit。この Field は URG bit がセットされた時のみに有効で、TCP データの中のどこに緊急で処理する必要があるものがあるかを 示します。この Field にセットされる値は、シーケンス番号 + 緊急処理を要するデータの位置 (シーケンス番号が 110 で、緊急処理を要するデータが TCP データの先頭から 440 Bytes 目だった場合、"550" が入る) です。
前述の通り、この Field をどのように活用するかはアプリケーション次第であり、TCP としては特に何も実行しません。
オプションについては、以下の記事で説明しています。
また、TCP の 3way handshake およびその後の http 通信のシーケンスについては以下の記事で紹介していますのでご参照下さい。
nesukeの推薦図書
以下に該当する人なら熟読することを強く推奨します。
- 周りのネットワークエンジニアよりも一歩抜き出た存在となり、価値を高めたい!
- サーバエンジニアだけどネットワークも含めた総合的なセキュリティへの理解も深めていきたい!
コメント
参考にさせてもらいました。具体的でわかりやすいです。
CWR bit、ECE bitの説明でRFC3268とありますがRFC3168ですかね。
公開ありがとうございます。