TCP協議關鍵資料結構
1、TCP協議頭資料結構
TCP協議頭資料結構是struct tcphdr,定義在include/linux/tcp.h中,主要包含源埠、目的埠、協議長度、控制標誌flags....
struct tcphdr { __be16 source; //源埠 __be16 dest; //目的埠 __be32 seq; //資料段的起始序列號 __be32 ack_seq; //確認序列號 #if defined(__LITTLE_ENDIAN_BITFIELD) //小端 __u16 res1:4, doff:4, //協議頭長度 fin:1, //斷開標誌 syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1; #elif defined(__BIG_ENDIAN_BITFIELD) //大端 __u16 doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1; #else #error "Adjust your <asm/byteorder.h> defines" #endif __be16 window; //視窗控制 __sum16 check; //阻塞控制 __be16 urg_ptr; };
2、TCP的控制緩衝區
TCP是完全非同步的協議,實際資料段的傳送獨立於來自所有套接字層的寫操作。TCP層分配的socket buffer來存放應用層寫入套接字的資料,但應用程式控制管理資料包的資訊存放在TCP控制緩衝區,TCP緩衝區由struct tcp_skb_cb資料結構描述,當資料從應用程式複製到TCP層的socket buffer時,函式需要TCP控制緩衝區中TCP協議頭的資訊來設定struct tcphdr資料結構中的相關資料域。
struct tcp_skb_cb { union { struct inet_skb_parm h4; //ip選項 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) struct inet6_skb_parm h6; #endif } header; /* For incoming frames */ __u32 seq; /* Starting sequence number 資料段起始序列號*/ __u32 end_seq; /* SEQ + FIN + SYN + datalen 最後一個數據段結束序列號*/ __u32 when; /* used to compute rtt's */ __u8 flags; /* TCP header flags. TCP協議頭中的flags */ /* NOTE: These must match up to the flags byte in a * real TCP header. */ #define TCPCB_FLAG_FIN 0x01 #define TCPCB_FLAG_SYN 0x02 #define TCPCB_FLAG_RST 0x04 #define TCPCB_FLAG_PSH 0x08 #define TCPCB_FLAG_ACK 0x10 #define TCPCB_FLAG_URG 0x20 #define TCPCB_FLAG_ECE 0x40 #define TCPCB_FLAG_CWR 0x80 __u8 sacked; /* State flags for SACK/FACK. 前送回答/選擇回答的狀態標誌*/ #define TCPCB_SACKED_ACKED 0x01 /* SKB ACK'd by a SACK block */ #define TCPCB_SACKED_RETRANS 0x02 /* SKB retransmitted */ #define TCPCB_LOST 0x04 /* SKB is lost */ #define TCPCB_TAGBITS 0x07 /* All tag bits */ #define TCPCB_EVER_RETRANS 0x80 /* Ever retransmitted frame */ #define TCPCB_RETRANS (TCPCB_SACKED_RETRANS|TCPCB_EVER_RETRANS) __u32 ack_seq; /* Sequence number ACK'd */ };
關鍵元素詳解:
seq:輸出資料段的起始序列號。
end_seq:最後一個輸出資料段結束序列號,結束序列號的值等於最後傳送的資料段序列號 + 一個SYN資料段 + 一個FIN資料段 + 資料段長度,即 end_seq = seq + SYN + FIN + 當前資料段長度。
when:用於計算RTT(Round-trip time)值,即資料段在網路上傳送時間,when資料域用來管理資料段傳輸起始計時和資料重傳,TCP資料段傳送方根據when的值計算出資料後要等待多長時間,如果等待一定時間後仍沒有收到接受段回覆的ACK,就重傳資料段。
flags:與TCP協議頭中的flags資料域定義相同。
sacked:儲存了選擇回答(SACK:Selective Acknowledge)和前送回答(FACK:Forward Acknowledge)的狀態標誌,有效標誌如下:
標誌 | 值 | 作用 |
TCPCB_SACKED_ACKED | 1 | SACK塊已經給出了skb資料緩衝區中的段回答資訊 |
TCPCB_SACKED_RESTRANS | 2 | 資料段需要重傳 |
TCPCB_LOST | 4 | 資料段丟失 |
TCPCB_TAGBITS | 結合前面三個標誌來標識資料段 | |
TCPCB_EVER_RETRANS | 0X80 | 指明資料段以前是否重傳過 |
TCPCB_RETRANS | 指明數段是一個重傳過的資料段 |
TCPCB_TAGBITS = TCPCB_SACKED_ACKED | TCPCB_SACKED_RESTRANS | TCPCB_LOST
3、TCP套接字資料結構
TCP套接字資料結構struct tcp_sock包含了管理TCP協議各方面的資訊,如傳送和接受方的序列號、TCP視窗尺寸、避免網路阻塞等。struct sock資料結構很龐大,在include/linux/tcp.h檔案中定義。
(1)inet_conn:INET協議族面向連線的套接字結構體,定義在include/net/inet_connection_sock檔案中,其中包含了sturct inet_connection_sock_af_ops *icsk_af_ops資料結構,這個資料結構是套接字操作函式指標,比如getsockopt函式和setsockopt函式指標。
struct inet_connection_sock_af_ops {
int (*queue_xmit)(struct sk_buff *skb);
void (*send_check)(struct sock *sk, struct sk_buff *skb);
int (*rebuild_header)(struct sock *sk);
int (*conn_request)(struct sock *sk, struct sk_buff *skb);
struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst);
int (*remember_stamp)(struct sock *sk);
u16 net_header_len;
u16 sockaddr_len;
int (*setsockopt)(struct sock *sk, int level, int optname,
char __user *optval, unsigned int optlen);
int (*getsockopt)(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen);
...
}
(2)tcp_hader_len:傳送資料段TCP協議頭的長度。
(3)xmit_size_goal:傳輸資料段你的目標。
(4)pred_flags:TCP協議預定向完成標誌。
(5)rcv_nxt:下一個輸入資料段序列號。
(6)snd_nxt:下一個傳送資料段的序列號。
(7)prequeue:輸入佇列。
(8)task:使用者程序,接受prequeue佇列中資料段的使用者程序。
(9)iov:向量指標,指向使用者地址空間中存放資料的陣列。
(10)memory:在prequeue佇列中所有socket buffer中資料長度的總和。
(11)Len:prequeue佇列上socket buffer緩衝區的個數。
(12)DMA,當網路裝置支援Scatter/Gather I/O功能,可以利用DMA直接訪問記憶體,將資料非同步從網路裝置硬體緩衝區複製到應用程式地址空間的緩衝區。
...
//DMA
#ifdef CONFIG_NET_DMA
/* members for async copy */
struct dma_chan *dma_chan;
int wakeup;
struct dma_pinned_list *pinned_list;
dma_cookie_t dma_cookie;
#endif
...
struct tcp_sock:
struct tcp_sock {
/* inet_connection_sock has to be the first member of tcp_sock */
struct inet_connection_sock inet_conn; //面向連線結構體
u16 tcp_header_len; /* Bytes of tcp header to send TCP協議頭長度*/
u16 xmit_size_goal_segs; /* Goal for segmenting output packets 輸出資料段目標*/
/*
* Header prediction flags
* 0x5?10 << 16 + snd_wnd in net byte order
*/
__be32 pred_flags;
u32 rcv_nxt; /* What we want to receive next */
u32 copied_seq; /* Head of yet unread data */
u32 rcv_wup; /* rcv_nxt on last window update sent */
u32 snd_nxt; /* Next sequence we send */
u32 snd_una; /* First byte we want an ack for */
u32 snd_sml; /* Last byte of the most recently transmitted small packet */
u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */
u32 lsndtime; /* timestamp of last sent data packet (for restart window) */
...
}
4、TCP協議選項Options
TCP協議是可配置協議,TCP選項可以通過setsockopt系統呼叫來設定,也可以通過getsockopt來返回當前TCP選項,TCP選項在struct tcp_sock資料結構中定義了,下面介紹TCP選項值的含義:
(1)、TCP_CORK/nonagle
這個選項對應struct tcp_sock中的nonagle資料域,如果配置了這個選項接受應用層的資料後TCP不會立即傳送資料段,知道資料段達到TCP協議資料段最大值,它使應用程式可以在路由的MTU小於TCP的資料段最大段大小(MSS)時停止傳送。TCP_CORK和TCP_NODELAY選項是互斥的。
(2)、TCP_DEFER_ACCEPT/defer_accept
應用層序呼叫者在資料還沒到達套接字之前,可以處於休眠狀態。但當資料到達套接字時應用程式被喚醒,如果等待超時應用層序也會被喚醒,盜用者設定一個時間值來描述應用程式等待資料到達的時間。該選項儲存struct sock 資料結構的defer_accept資料域。
(3)、TCP_INFO
使用此選項可以獲取大部分套接字的配置資訊,獲取的配置資訊儲存在struct tcp_info資料結構中:
struct tcp_info {
__u8 tcpi_state; //當前tcp連線狀態
__u8 tcpi_ca_state;
__u8 tcpi_retransmits;
__u8 tcpi_probes;
__u8 tcpi_backoff;
__u8 tcpi_options;
__u8 tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
__u32 tcpi_rto;
__u32 tcpi_ato;
__u32 tcpi_snd_mss;
__u32 tcpi_rcv_mss;
__u32 tcpi_unacked;
__u32 tcpi_sacked;
__u32 tcpi_lost;
__u32 tcpi_retrans;
__u32 tcpi_fackets;
/* Times. */
__u32 tcpi_last_data_sent; //最近傳送資料的時間戳
__u32 tcpi_last_ack_sent; /* Not remembered, sorry. */
__u32 tcpi_last_data_recv; //最近接受資料的時間戳
__u32 tcpi_last_ack_recv;
/* Metrics. */
__u32 tcpi_pmtu; //mtu
__u32 tcpi_rcv_ssthresh;
__u32 tcpi_rtt;
__u32 tcpi_rttvar;
__u32 tcpi_snd_ssthresh;
__u32 tcpi_snd_cwnd;
__u32 tcpi_advmss;
__u32 tcpi_reordering;
__u32 tcpi_rcv_rtt;
__u32 tcpi_rcv_space;
__u32 tcpi_total_retrans;
};
(4)、TCP_KEEPCNT
此選項可以設定TCP在斷開連線之前可以通過套接字傳送多少個保持連線活動(keepalive)的探測資料段,該選項存放在struct tcp_sock資料結構中的keepalive_probes資料域,如果設定了該選項還要設定套接字層的SO_KEEPALIVE選項。
(5)、TCP_KEEPIDEL
TCP開始傳送連線是否保持活動的探測資料段之前,連線處於空閒狀態的時間值,保持在struct tcp_sock資料結構中的keepalive_time資料域,預設值是2個小時,如果設定此選項還要設定套接字層的SO_KEEPALIVE選項。
(6)、TCP_KEEPINTVL
設定兩次傳送探測連線保持活動資料段之前要等待多少秒,該值存放在struct tcp_sock資料結構的deepalive_intvl資料域中,初始值是75秒。
(7)TCP_LINGER2
此選項指處於FIN_WAIT2狀態的孤立套接字還應保持存活多長時間,如果是0則關閉選項,linux使用常規方式處理FIN_WAIT_2和TIME_WAIT狀態,如果值小於0,則套接字立即從FIN_WAIT2狀態進入CLOSED狀態,不經過FIN_WAIT,此選項儲存在struct tcp_sock資料結構的liinger2資料域中,預設值由sysctl決定。
(8)、TCP_NODELAY
如果設定了該選項則TCP會立即把資料傳送到網路層,而不會等待資料達到pmtu才傳送,改值儲存在struct tcp_sock資料結構的nonagle資料域中,和TCP_CORK互斥。
(9)、TCP_MAXSEG
此選項指定TCP最大資料段的大小值,TCP的MSS值也是此選項決定,但MSS的值不能超過MTU,TCP連線兩端可以協商資料段大小。
(10)、TCP_QUICKACK
當設定這個值為1,就會關閉延遲迴答,延遲迴答是linux TCP的一個常規模式。延遲迴答時ACK資料會延遲到可以與一個等待發送到另一端的資料段合併時,才會傳送出去,如果設定為1,sruct tcp_sock資料結構中的ack部分的pingpong資料域設為0,就可以禁止延遲傳送。
(11)、TCP_SYNCNT
這個選項是TCP在嘗試建立連線,如果連線沒有建立起來,要重傳多少此SYN包後就放棄建立連線請求,該選項儲存在struct tcp_syn_retries資料域中。
(12)、TCP_WINDOW_CLAMP
指定套接字視窗大小,視窗的最小值是SOCK_MIN_RCVBUF除以2,等於128個位元組,該選項儲存在struct tcp_sock資料結構中的windown_clamp資料域。
4、struct msghdr
應用層傳給套接字的資訊結構體是struct msghdr,在傳送處理函式中把資料從應用層複製到核心地址空間,結構體定義在include/linux/socket.c檔案中
struct msghdr {
void * msg_name; /* Socket name 套接字名字,也是對端ip地址*/
int msg_namelen; /* Length of name 目的地址長度*/
struct iovec * msg_iov; /* Data blocks 應用層緩衝區起始地址 */
__kernel_size_t msg_iovlen; /* Number of blocks 緩衝區陣列個數 */
void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) 控制資訊*/
__kernel_size_t msg_controllen; /* Length of cmsg list 控制資訊長度*/
unsigned msg_flags; //接受資料的標誌
};
msg_name:套接字名字,不是實際套接字名,他是一個指標,指向struct sockaddr_in資料結構的變數,struct sockaddr_in資料結構包含了傳送資料段的目標IP地址和埠號。
msg_namelen:msg_name指向的地址資訊長度。
msg_iov:指向應用層緩衝區陣列的起始地址。
msg_iovlen:msg_iov緩衝區陣列中緩衝區的個數。
msg_control:儲存向套接字層下發的協議控制資訊。
msg_flags:套接字從應用層接受到的控制標誌。
標誌 | 值 | 說明 |
MSG_OOB | 1 | 請求out-of-bound資料 |
MSG_PEEK | 2 | 從接受佇列中返回資料,但不把資料從佇列中移走 |
MSG_DONTROUTE | 4 | 不對該資料路由,常用於ping歷程中傳送ICMP資料包 |
MSG_TRYHARD | 4 | 不用於TCP/IP協議棧,與MSG_DONTROUTE同義 |
MSG_CTRUNC | 8 | 用於SOL_IP內部控制資訊 |
MSG_PROBE | 0x10 | 用於發現MTU資料段 |
MSG_TRUNC | 0x20 | truncate訊息 |
MSG_NONTWAIT | 0x40 | 呼叫應用程式是否用於不被阻塞的I/O |
MSG_EOR | 0x80 | 資訊結束 |
MSG_WAITALL | 0x100 | 在返回資料前等待所有資料到達 |
MSG_FIN | 0x200 | TCP結束資料段(FIN) |
MSG_SYN | 0x800 | TCP同步資料段(SYN) |
MSG_CONFIRM | 0x800 | 傳送資料包前確認路徑有效 |
MSG_RST | 0x1000 | TCP復位連線資料段(RST) |
MSG_ERRQUEUE | 0x2000 | 從錯誤佇列讀取資料段 |
MSG_NOSIGNAL | 0x4000 | 當確認連線斷開,不產生SIGPIPE訊號 |
MSG_MORE | 0x8000 | 指明將傳送更多的資料資訊 |