Try   HackMD

TCP - TCP Header

課程影片

第 8D 講 TCP 與網路阻塞偵測與控制技術 L08 4

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

How TCP Works - Window Scaling and Calculated Window Size

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

How TCP Works - FINs vs Resets

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Python Network Packet Sniffer Tutorial - 5 - Unpacking ICMP and TCP Data

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

TCP Header


       0                   1                   2                   3
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |          Source Port          |       Destination Port        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                        Sequence Number                        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                    Acknowledgment Number                      |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  Data |           |U|A|P|R|S|F|                               |
      | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
      |       |           |G|K|H|T|N|N|                               |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |           Checksum            |         Urgent Pointer        |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                    Options                    |    Padding    |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                             data                              |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

而 Linux 核心定義在 include/uapi/linux/tcp.h 中。這個結構體中,有針對 big endianlittle endian 定義欄位。為了方便,這邊省略了 little endian 時的定義:

struct tcphdr {
	__be16	source;
	__be16	dest;
	__be32	seq;
	__be32	ack_seq;
	__u16	doff:4,
		res1:4,
		cwr:1,
		ece:1,
		urg:1,
		ack:1,
		psh:1,
		rst:1,
		syn:1,
		fin:1;
	__be16	window;
	__sum16	check;
	__be16	urg_ptr;
};

除此之外,對應欄位的 mask 也有定義:

enum {
	TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000),
	TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000),
	TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000),
	TCP_FLAG_ACK = __constant_cpu_to_be32(0x00100000),
	TCP_FLAG_PSH = __constant_cpu_to_be32(0x00080000),
	TCP_FLAG_RST = __constant_cpu_to_be32(0x00040000),
	TCP_FLAG_SYN = __constant_cpu_to_be32(0x00020000),
	TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000),
	TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000),
	TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000)
};

sourcedest

這兩個欄位紀錄的各自是「從發送方的哪個 port 出發」以及「送往接收方的哪個 port」。老樣子,因為 IP 位址已經紀錄在 L3 的 IP header 中了,所以這兩個欄位只會紀錄 port 的編號,不會紀錄 IP。

seq - 滑動視窗演算法使用的編號

TCP 的可靠傳輸使用滑動視窗演算法。而使用滑動視窗演算法中,每個封包需要使用一個 sequence number,而這個 seq 欄位就是這個 sequence number

而與前述的滑動視窗演算法稍有不同的是:TCP 提供的傳輸服務是一個「位元流」,所以這個 seq 並不是指封包的編號,而是指「底下 Data 的第一個位元,在整個位元流中的位置」。

ackack_seq - Cumilated ACK 的編號

如果 ack 位元被設為 1,表示這個封包會是個 ACK,而這時 qck_seq 欄位表示的就是 cumilated ACK 中,表達「這個 sequence number 以前的資料,我都收到了」的那個 sequence number。與上述的 seq 欄位類似,這個 ack_seq 註記的 sequence number,並不是封包的編號,而是「到這個位元流的哪個位置以前的資料,現在都收到了」。

要注意的事情是:只有在 ack 欄位被設為 1 時,這個 ack_seq 才會表示這個封包是一個 ACK,而這個 ack_seq 欄位才會表示上述的意義。否則這個欄位中的資訊不會被使用。

synfin - 建立與結束連線

如果 syn 欄位是 1,表示這個封包是用來請求建立連線; 類似地,若 fin 欄位是 1,就表示這個封包是用來結束一個連線。

psh - 清空緩衝區

這裡「清空」的意思不是移除資料,而是 flush。也就是「不論 buffer 中的資料有沒有滿,都立刻將裡面的資料移至下個階段處理」。

以發送方收到 psh 被設為 1 的封包為例。前面有提到:TCP 傳輸時,發送方的資料會先被 buffer 起來,直到累積到一定的量才會被打包為 TCP 封包傳輸。而當發送方從接收方收到一個 psh 被設定的封包時,buffer 中的資料會直接移給下一個階段,也就是強制打包傳送給發送方。不論發送方當下的 buffer 裡面的東西有沒累積到那個量。

而另外一方面,如果接收方從發送方收到一個 psh 被設定為 1 的封包,那麼不論接收方的 buffer 中有沒有滿,裡面的資料都要移往那台電腦 TCP stack 的下一個階段,把用來接收的 buffer 讓出來。

urgurg_ptr - 緊急資料

如果 urg 被設定,表示這個封包的 Data 欄位中的第 urg_ptr 開始,有緊急的資料。

window - 發送方能傳送的「額度」

這個 16 位元的欄位用來讓接收方告訴發送方最多還可以再送多少位元組的資料,以用來做 flow control。要注意的是:這個欄位是拿來做 flow control,也就是用來讓接收方與發送方協調傳輸步調,而不是拿來做 congestion control。「整個網路有多壅塞」這件事情並沒有在 TCP header 中紀錄,而是以其他機制來判斷。