どうやってIPからMACアドレスを解決するか - ARPの挙動を調べた - $shibayu36->blog;はデータリンク層、tracerouteの仕組みをtcpdumpとwiresharkで理解する - $shibayu36->blog;はネットワーク層について実践してみたので、続いてトランスポート層について実践してみたい。そこで今回はcurl http://www.example.com/
したときのTCPパケットの様子を観察し、理解してみることにした。ネットワーク初心者であるので、正しいかは不明。また、概要をつかみたいだけなので、詳細はあまり立ち入らないことにする。
まずパケットキャプチャ。dig www.example.comすると、IPは93.184.216.34ということが分かるので、以下のコマンドでキャプチャしておく。
tcpdump -w dump.cap -n -i en0 'host 93.184.216.34'
その後dump.capをWiresharkに食わせる。
このパケットを順番一つずつ眺めてみる。なお、手元PCをクライアント、www.example.comをサーバと呼ぶ。
3 way handshake完了まで
1 0.000000 192.168.10.7 93.184.216.34 TCP 78 53451 → 80 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=32 TSval=635755241 TSecr=0 SACK_PERM=1
クライアントからサーバへのSYNパケット。MSSでTCPパケット一つの上限サイズを通知している。Winによりどれだけ並列に送ってよいか通知している。WSやSACK_PERMの意味についてはあまり調べなかった。Wireshark Q&A とかが参考になりそう。
2 0.120085 93.184.216.34 192.168.10.7 TCP 74 80 → 53451 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=1460 SACK_PERM=1 TSval=467756484 TSecr=635755241 WS=512
サーバからクライアントへのSYN + ACKパケット。クライアントからのSYNに対するACK応答と、クライアントへのSYNを同時に行っている事がわかる。こちらもMSSやWinを通知している。
3 0.120245 192.168.10.7 93.184.216.34 TCP 66 53451 → 80 [ACK] Seq=1 Ack=1 Win=131744 Len=0 TSval=635755360 TSecr=467756484
サーバからクライアントへのSYN要求へのACK応答。ちなみにこのパケットの中身をもうちょっと見ると、Window size value: 4117
というのが見えたので、4117並列で送ってもいいということなのかな?
この三つのパケットで、3way handshakeがなされたので、コネクションが確立される。ここで、MSSはどちらも1460だったので、TCPの1パケットのサイズは1460を上限とする。サイズが一致していなければ小さい方を使うはず。
HTTPデータのやり取り完了まで
続いて、リクエストの送信。
4 0.120685 192.168.10.7 93.184.216.34 HTTP 141 GET / HTTP/1.1 Transmission Control Protocol, Src Port: 53451, Dst Port: 80, Seq: 1, Ack: 1, Len: 75 Source Port: 53451 Destination Port: 80 [Stream index: 0] [TCP Segment Len: 75] Sequence number: 1 (relative sequence number) [Next sequence number: 76 (relative sequence number)] Acknowledgment number: 1 (relative ack number) 1000 .... = Header Length: 32 bytes (8) Flags: 0x018 (PSH, ACK) Window size value: 4117 [Calculated window size: 131744] [Window size scaling factor: 32] Checksum: 0xd192 [unverified] [Checksum Status: Unverified] Urgent pointer: 0 Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps [SEQ/ACK analysis] TCP payload (75 bytes) Hypertext Transfer Protocol
HTTPのプロトコルで、GET /しているだけ。PSHフラグが立っているので、すぐさま上位のアプリケーション層に渡される?(第15回 信頼性のある通信を実現するTCPプロトコル(2):基礎から学ぶWindowsネットワーク(2/3 ページ) - @IT)サイズが1460を越えていないので、このパケットのみでリクエストの送信は完了。
次はHTTPレスポンスのデータのTCPパケット。
5 0.248376 93.184.216.34 192.168.10.7 TCP 1514 80 → 53451 [PSH, ACK] Seq=1 Ack=76 Win=144896 Len=1448 TSval=467756515 TSecr=635755360 [TCP segment of a reassembled PDU] Transmission Control Protocol, Src Port: 80, Dst Port: 53451, Seq: 1, Ack: 76, Len: 1448 Source Port: 80 Destination Port: 53451 [Stream index: 0] [TCP Segment Len: 1448] Sequence number: 1 (relative sequence number) [Next sequence number: 1449 (relative sequence number)] Acknowledgment number: 76 (relative ack number) 1000 .... = Header Length: 32 bytes (8) Flags: 0x018 (PSH, ACK) Window size value: 283 [Calculated window size: 144896] [Window size scaling factor: 512] Checksum: 0x23b3 [unverified] [Checksum Status: Unverified] Urgent pointer: 0 Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps [SEQ/ACK analysis] TCP payload (1448 bytes) [Reassembled PDU in frame: 7] TCP segment data (1448 bytes)
TCP segment dataが1448bytes返ってきている。MSSの値を越えないように送られてきているので、これだけではHTTPレスポンス全体ではない。このデータ送信でもAcknowledgment numberが76で返ってきているので、先程ローカルから送ったリクエストのパケットに対するACKの役割も担えていることが面白い。
続いて4番目のパケットのGET /のリクエストに対応するACK。5番目のHTTPレスポンスデータのTCPパケットでACKの役割も担えていたので、実質意味はなさそう。
6 0.248385 93.184.216.34 192.168.10.7 TCP 66 80 → 53451 [ACK] Seq=1 Ack=76 Win=144896 Len=0 TSval=467756515 TSecr=635755360
次がHTTPレスポンスデータの二つ目のパケット。5番目のパケットと合わせてレスポンスデータが揃ったのでHTTPとして認識されている。このデータもPSHフラグがONになっているので、すぐに上位の層に送られそう。
7 0.248387 93.184.216.34 192.168.10.7 HTTP 211 HTTP/1.1 200 OK (text/html) Frame 7: 211 bytes on wire (1688 bits), 211 bytes captured (1688 bits) Ethernet II, Src: NecPlatf_8d:b1:70 (98:f1:99:8d:b1:70), Dst: Apple_42:64:b2 (8c:85:90:42:64:b2) Internet Protocol Version 4, Src: 93.184.216.34, Dst: 192.168.10.7 Transmission Control Protocol, Src Port: 80, Dst Port: 53451, Seq: 1449, Ack: 76, Len: 145 Source Port: 80 Destination Port: 53451 [Stream index: 0] [TCP Segment Len: 145] Sequence number: 1449 (relative sequence number) [Next sequence number: 1594 (relative sequence number)] Acknowledgment number: 76 (relative ack number) 1000 .... = Header Length: 32 bytes (8) Flags: 0x018 (PSH, ACK) Window size value: 283 [Calculated window size: 144896] [Window size scaling factor: 512] Checksum: 0xc810 [unverified] [Checksum Status: Unverified] Urgent pointer: 0 Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps [SEQ/ACK analysis] TCP payload (145 bytes) TCP segment data (145 bytes) [2 Reassembled TCP Segments (1593 bytes): #5(1448), #7(145)] Hypertext Transfer Protocol Line-based text data: text/html
レスポンスデータの一つ目(5番目のパケット)に対応するクライアントからサーバへのACK。
8 0.248952 192.168.10.7 93.184.216.34 TCP 66 53451 → 80 [ACK] Seq=76 Ack=1449 Win=130304 Len=0 TSval=635755486 TSecr=467756515
レスポンスデータの一つ目(5番目のパケット)に対応するクライアントからサーバへのACK。なぜもう一度送られているのかがいまいちわからなかった。
9 0.248953 192.168.10.7 93.184.216.34 TCP 66 [TCP Dup ACK 8#1] 53451 → 80 [ACK] Seq=76 Ack=1449 Win=130304 Len=0 TSval=635755486 TSecr=467756515
レスポンスデータの二つ目(7番目のパケット)に対応するクライアントからサーバへのACK。
10 0.249033 192.168.10.7 93.184.216.34 TCP 66 53451 → 80 [ACK] Seq=76 Ack=1594 Win=130144 Len=0 TSval=635755486 TSecr=467756515
これでHTTPのデータのやり取りは終わり。この部分のイメージは
コネクション切断
最後にコネクション切断。
クライアントからサーバへのFIN。
11 0.249555 192.168.10.7 93.184.216.34 TCP 66 53451 → 80 [FIN, ACK] Seq=76 Ack=1594 Win=131072 Len=0 TSval=635755487 TSecr=467756515
サーバからクライアントへのACK/FIN。これまでのサーバからのACKの番号は76だったので、先程のFINに対応するようにACKの番号が1増えて77になってそう。
12 0.444726 93.184.216.34 192.168.10.7 TCP 66 80 → 53451 [FIN, ACK] Seq=1594 Ack=77 Win=144896 Len=0 TSval=467756546 TSecr=635755487
クライアントからサーバへのACK。こちらもACKの番号が1増えている。
13 0.444847 192.168.10.7 93.184.216.34 TCP 66 53451 → 80 [ACK] Seq=77 Ack=1595 Win=131072 Len=0 TSval=635755681 TSecr=467756546
これで全てのパケットのやり取りが終わった。
まとめ
今回はHTTP GETしたときのTCPパケットの様子を眺めてみた。これまでは単純に3way handshakeがなされているとか、データはパケットに分割されているとか、そういう浅い理解しか無かった。しかし、今回様子を実際に眺めることで、SYNの時にデータサイズや並列数も通知されるとか、パケットの到着順によりACKも兼ねてしまうことがあるとか、そのような現象を体感できた。
今回はwww.example.comへのアクセスだったので、このように非常にシンプルなパケットの様子になっているが、もう少しデータ量が大きいページへアクセスすると、途中で並列数を変更するパケットが流れる様子なども観察できた。機会があったらもう少し詳しく調べてみたい。