IP/TCPヘッダーのフォーマットについて
IPヘッダのフォーマットは以下のページの絵が綺麗
【第3回 IPヘッダの解読法とパケット分割/キーマンズネット】 http://www.keyman.or.jp/3w/prd/42/30002442/
TCPヘッダーのフォーマットは、以下のページの絵が見やすい
【Transmission Control Protocol - Wikipedia】 http://ja.wikipedia.org/wiki/Transmission_Control_Protocol
iptrace トレースだと、IPヘッダーのダンプの後、TCPヘッダーのダンプが表示される。両方とも”オプション”の部分は、通常はデータが入らず(ネット上の解説もそうだし、実際のトレースもそう)、それぞれサイズとしては、20byte の大きさである。
なので、ACKの様な付随するデータの無いパケットだと、20Byte + 20 Byte で 40Byteのパケット・サイズが一般的。
IPヘッダーの中に、”IPヘッダーレングス(IHL)”というのがあるが、これは、TCPのヘッダーは含まない。
一方で、同じIPヘッダー中の”Total Length” はTCPヘッダーもデータも含む全体なので、少しこんがらがる。
細かい通信のやりとりについて
全体を通して、Seqやack\の番号がどの様に振られるかが、一番理解が大変だが、以下のサイトの説明が良かった。
【@IT:TCP/IPアレルギー撲滅ドリル【下位レイヤ編】6-2】 http://www.atmarkit.co.jp/fnetwork/rensai/tcp13/02.html
ここからは、詳しく解説しているサイトが無かったため、いろいろ考えた結果、多分こうだろう。と言う理解。
実際のトレースでは Webサイトに載っているような教科書通りの動きとはちょっと違った。教科書に載っている例と違い、ACK を返す必用が無い場合でも、ACKのビットが立っている事が多かった。
また通信終了の FIN → FIN/ACK → ACK という教科書通りの綺麗な流れは目撃できなかった。Dataが送り終わったら、Dataが届いた ACK を待たずに、いきなり FIN を送るトレースしか見つけられなかった。(といっても2つしかサンプルを見ていないので、なんとも言えないが)
以下は実際のトレースを元に、seq と ackの 番号を見やすくふり直したもの。クライアントからサーバーに 120Byte のデータを送るだけの通信。
クライアント |
サーバー |
||||||||
---|---|---|---|---|---|---|---|---|---|
No |
flg |
seq |
ack |
Len |
flg |
seq |
ack |
Len |
|
1 |
SYN |
1000 |
0 |
→ |
|||||
2 |
← |
SYN | ACK |
2000 |
1001 |
|||||
3 |
ACK |
1001 |
2001 |
→ |
|||||
4 |
PSH | ACK |
1001 |
2001 |
120 |
→ |
||||
5 |
FIN | ACK |
1121 |
2001 |
→ |
|||||
6 |
← |
ACK |
2001 |
1122 |
|||||
7 |
← |
FIN | ACK |
2001 |
1122 |
|||||
8 |
ACK |
1122 |
2001 |
→ |
各ステップの勝手な解説は以下の通り。
No | コメント |
1 | Client側のseqの開始番号は、環境と状況による。 通信開始時の ack も、綺麗に0で始まることは無い。ここでは見やすさのため0にする。 |
2 | Server側のseqの開始番号は、以前の通信と状況に依存。 ack の番号は、ACK対象のseqの番号に対して+1するのが作法。 |
3 | seq番号は、直前にもらった ack の値になる。ackの値は、ACKを返すべき seq に +1した値を入れる。 ここまでが、3ハンドシェイクと言う。 |
4 | DataのPSH(PUSH)。 教科書だとPSHフラグだけだったりするが、実際には、ACKのフラグも立っていた。 ただし、この ACKフラグには、意味はなさそう。 |
5 | 教科書だと、ここでサーバーからACKを返したりするが現実には、クライアントは、データの受信確認を待たずに、もう送るデータが無い事を示すFINを送る事が多い。seqは、自分が送ったバイト数だけ加算されている。 ここでも、ACKが立っているが、値は、SYN/ACKのハンドシェイク終了後と変わっていない。サーバー側から特に何も受け取っていないのでack の値は変わらない。 |
6 | ここで漸く、ACKが返るが、No.4に対するACKならack=1121になるはずだが、実際は、1122になる。恐らくNo.4に対するACKを返す前にNo.5が来たため、No.4/No.5へのACKをまとめて返している。と理解。
つまり、ack の値の算出は、 1001 + 120(No.4のDataへの応答) + 1(No.5のFINほの応答) = 1122 DataのACKをまとめて返す例は Webにあったが、DataとFINのACKをまとめて返す例は、Webでは、みつからなかった。ただ実際のトレースを見ると、その様に解釈を割り当てる以外に今の所思いつかない。 |
7 | No.6と seqも、ack も同じ。 FINが送りたいだけだと思われる。No.6でACKを返しているので、ACKフラグも立てる必用が無いと思うが、実際には立っていた。 |
8 | 通信終了 |
seq/ack の番号を理解するための法則
- SYN/FIN に対してACKを返す場合は、ackの値として、ACKを返す対象の seq番号に 1足した値 を ack にセットして返す。
- データを受け取った時にACKを返す場合は、データの長さ + seq 番号を入れた値を ack にセットして返す。
- 最期に受け取った ACKの ack の値が、次のseq の値になる。反対に言うと、ACKの ack に値1001を入れて返すという事は、相手に次の通信の seq は、1001から始めて下さい。(1000まで受け取りました)という事を表す。
- seqは、自分が、SYNや、PSH を送ると1加算される。Dataを送るとDataのバイト数分だけ加算される。ACKを返した時のみ、何も加算されない。
データのサイズの計算方法
送ったデータサイズは、ダンプされているデータの量を数えるか、IPヘッダー(TCPヘッダーでは無い)の IHL (IP Header Lengt) と、IP Length と、TCPヘッダーの長さから計算する。
データをやりとりする時に、ack の値に加算されるのは、IP/TCPヘッダーを含まない部分のデータサイズ(つまり本当のデータ部分である)
IP Length = IP ヘッダーの長さ(IHL) (1) + TCP ヘッダー の長さ(2) + Dataサイズ (3)
(2)のTCPヘッダーの長さだけが、トレースからは読みにくい。TCPヘッダー内のData Offset という項目が、TCPヘッダー長を表すが、32bit ワード単位(4バイト単位)であるため、もし DataOffset =5 であれば、 4 Byte x 5 = 20 byte になる。
大抵は (1) も (2) も 20byte のサイズである事が多い。オプションが付く時だけ、(1)、(2)のサイズは 20byte 以外の値になる。
コネクション切断処理
No.6とNo.7 のステップはひとまとめにできる気がする。が、トレースからは、これらがまとまる例は、まだ見ていない。
下記のサイトの資料のPage 7にも、典型的な FIN/ACKの例として同様な動きが解説されているので、これは「こういうもの」として理解しておく。