# TCP - TCP Header [TOC] ## 課程影片 ### 第 8D 講 TCP 與網路阻塞偵測與控制技術 L08 4 {%youtube IP5GKwaVIhs %} ### How TCP Works - Window Scaling and Calculated Window Size {%youtube 2PJVHvthrNU %} ### How TCP Works - FINs vs Resets {%youtube -vgk9P-6dPY %} ### [Python Network Packet Sniffer Tutorial - 5 - Unpacking ICMP and TCP Data](https://youtu.be/3zwuOo7U1YQ) {%youtube 3zwuOo7U1YQ %} ## 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`](https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/tcp.h#L25) 中。這個結構體中,有針對 *big endian* 或 *little endian* 定義欄位。為了方便,這邊省略了 *little endian* 時的定義: ```c 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* 也有定義: ```c 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) }; ``` ### `source` 與 `dest` 這兩個欄位紀錄的各自是「從發送方的哪個 *port* 出發」以及「送往接收方的哪個 *port*」。老樣子,因為 *IP* 位址已經紀錄在 L3 的 *IP header* 中了,所以這兩個欄位只會紀錄 *port* 的編號,不會紀錄 IP。 ### `seq` --- 滑動視窗演算法使用的編號 *TCP* 的可靠傳輸使用滑動視窗演算法。而使用滑動視窗演算法中,每個封包需要使用一個 *sequence number*,而這個 `seq` 欄位就是這個 *sequence number*。 而與前述的滑動視窗演算法稍有不同的是:*TCP* 提供的傳輸服務是一個「位元流」,所以這個 `seq` 並不是指封包的編號,而是指「底下 *Data* 的第一個位元,在整個位元流中的位置」。 ### `ack` 與 `ack_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` 欄位才會表示上述的意義。否則這個欄位中的資訊不會被使用。 ### `syn` 與 `fin` --- 建立與結束連線 如果 `syn` 欄位是 `1`,表示這個封包是用來請求建立連線; 類似地,若 `fin` 欄位是 `1`,就表示這個封包是用來結束一個連線。 ### `psh` --- 清空緩衝區 這裡「清空」的意思不是移除資料,而是 *flush*。也就是「不論 *buffer* 中的資料有沒有滿,都立刻將裡面的資料移至下個階段處理」。 以發送方收到 `psh` 被設為 `1` 的封包為例。前面有提到:*TCP* 傳輸時,發送方的資料會先被 *buffer* 起來,直到累積到一定的量才會被打包為 *TCP* 封包傳輸。而當發送方從接收方收到一個 `psh` 被設定的封包時,*buffer* 中的資料會直接移給下一個階段,也就是強制打包傳送給發送方。不論發送方當下的 *buffer* 裡面的東西有沒累積到那個量。 而另外一方面,如果接收方從發送方收到一個 `psh` 被設定為 `1` 的封包,那麼不論接收方的 *buffer* 中有沒有滿,裡面的資料都要移往那台電腦 *TCP stack* 的下一個階段,把用來接收的 *buffer* 讓出來。 ### `urg` 與 `urg_ptr` --- 緊急資料 如果 `urg` 被設定,表示這個封包的 *Data* 欄位中的第 `urg_ptr` 開始,有緊急的資料。 ### `window` --- 發送方能傳送的「額度」 這個 16 位元的欄位用來讓接收方告訴發送方最多還可以再送多少位元組的資料,以用來做 *flow control*。要注意的是:這個欄位是拿來做 *flow control*,也就是用來讓接收方與發送方協調傳輸步調,而不是拿來做 *congestion control*。「整個網路有多壅塞」這件事情並沒有在 *TCP header* 中紀錄,而是以其他機制來判斷。