TCP建立连接——连接请求块

TCP连接建立系列 — 连接请求块

分类: TCP/IP Kernel   1875人阅读  评论(1)  收藏  举报
TCPIP linux内核

目录(?)[+]

连接请求块(request_sock)之于TCP三次握手,就如同网络数据包(sk_buff)之于网络协议栈,都是核心的数据结构。

内核版本:3.6

Author:zhangskd @ csdn blog

 

存储队列

 

连接请求块的存储队列:包括全连接队列、半连接队列。

[java]  view plain copy
  1. /** 
  2.  * @icsk_accept_queue: FIFO of established children 
  3.  */  
  4. struct inet_connection_sock {  
  5.     ...  
  6.     /* 存放SYN_RECV、ESTABLISHED状态的连接请求块tcp_request_sock */  
  7.     struct request_sock_queue icsk_accept_queue;  
  8.     ...  
  9. };  
[java]  view plain copy
  1. /* struct request_sock_queue - queue of request_sock 
  2.  * @rskq_accept_head: FIFO head of established children 
  3.  * @rskq_accept_tail: FIFO tail of established children 
  4.  * @syn_wait_lock: serializer 
  5.  * @rskq_defer_accept: User waits for some data after accept() 
  6.  */  
  7.   
  8. struct request_sock_queue {  
  9.     struct request_sock *rskq_accept_head; /* ESTABLISHED状态请求块的头,等待accept */  
  10.     struct request_sock *rskq_accept_tail; /* ESTABLISHED状态请求块的尾 */  
  11.   
  12.     rwlock_t syn_wait_lock; /* listen_opt及其成员的读写锁 */  
  13.     u8 rskq_defer_accept;  
  14.   
  15.     /* 3 byte hole, try to pack */  
  16.     struct listen_sock *listen_opt; /* SYN_RECV状态的请求块,等待ACK */  
  17. };  

 

连接请求块(request_sock)保存在两个队列中:

(1) 处于SYN_RECV状态,即半连接队列

      保存在icsk->icsk_accept_queue.listen_opt中,这个listen_sock实例在listen()后创建。

      当客户端的ACK到达时,连接请求块会被移动到ESTABLISHED状态的连接请求块队列中。

      注意,半连接队列是在listen()时创建的。

(2) 处于ESTABLISHED状态,即全连接队列

      保存在icsk->icsk_accept_queue.rskq_accept_head和icsk->icsk_accept_queue.rskq_accept_tail

      所指定的FIFO队列中。此队列的连接请求块等待accept()的获取。

 

listen_sock用于保存SYN_RECV状态的连接请求块,它的实例在listen()时创建。

[java]  view plain copy
  1. /* struct listen_sock - listen state 
  2.  * 
  3.  * @max_qlen_log - log_2 of maximal queued SYNs/REQUSTs 
  4.  */  
  5.   
  6. struct listen_sock {  
  7.     u8 max_qlen_log; /* 半连接队列最大长度的log2 */  
  8.     u8 synflood_warned; /* SYN Flood标志 */  
  9.     /* 2 bytes hole, try to use */  
  10.     int qlen; /* 当前连接请求块的数目 */  
  11.     int qlen_young; /* 当前未重传过SYNACK的请求块数目 */  
  12.     int clock_hand;  
  13.     u32 hash_rnd; /* 一个随机数,用于计算hash值 */  
  14.     u32 nr_table_entries; /* 半连接队列最大长度 */  
  15.     struct request_sock *syn_table[0]; /* 连接请求块的指针数组 */  
  16. };  

 

连接请求块

 

最基本表示。

[java]  view plain copy
  1. /* struct request_sock - mini sock to represent a connection request */  
  2. struct request_sock {  
  3.     struct request_sock *dl_next; /* Must be first member! */  
  4.   
  5.     u16 mss; /* 客户端通告的MSS */  
  6.     u8 retrans; /* SYNACK的重传次数 */  
  7.     u8 cookie_ts; /* syncookie: encode tcpopts in timestamp */  
  8.     u32 window_clamp; /* 本端的最大通告窗口 */  
  9.     u32 rcv_wnd; /* 本端的接收窗口大小 */  
  10.     u32 ts_recent; /* 下个发送段的时间戳回显值 */  
  11.     unsigned long expires; /* SYNACK的超时时间 */  
  12.     const struct request_sock_ops *rsk_ops; /* 指向tcp_request_sock_ops,操作函数 */  
  13.     struct sock *sk; /* 连接建立之前无效 */  
  14.     u32 secid;  
  15.     u32 peer_secid;  
  16. };  

inet层表示。

[java]  view plain copy
  1. struct inet_request_sock {  
  2.     struct request_sock req;  
  3.   
  4. #if IS_ENABLED(CONFIG_IPV6)  
  5.     u16 inet6_rsk_offset;  
  6. #endif  
  7.   
  8.     __be16 loc_port; /* 本端端口 */  
  9.     __be32 loc_addr; /* 本端IP */  
  10.     __be32 rmt_addr; /* 客户端IP */  
  11.     __be16 rmt_port; /* 客户端端口 */  
  12.   
  13.     kmemcheck_bitfield_begin(flags);  
  14.     u16 snd_wscale : 4/* 对端的扩大因子 */  
  15.         rcv_wscale : 4/* 本端的扩大因子 */  
  16.         tstamp_ok : 1/* 连接是否支持TIMESTAMP选项 */  
  17.         sack_ok : 1/* 连接是否支持SACK选项 */  
  18.         wscale_ok : 1/* 连接是否支持Window Scale选项 */  
  19.         ecn_ok : 1/* 连接是否支持ECN选项 */  
  20.         acked : 1,  
  21.         no_srccheck : 1;  
  22.     kmemcheck_bitfield_end(flags);  
  23.   
  24.     struct ip_options_rcu *opt; /* IP选项 */  
  25. };  

TCP层表示为tcp_request_sock。

[java]  view plain copy
  1. struct tcp_request_sock {  
  2.     struct inet_request_sock req;  
  3.   
  4. #ifdef CONFIG_TCP_MD5SIG  
  5.     /* Only used by TCP MD5 Signature so far. */  
  6.     const struct tcp_request_sock_ops *af_specific;  
  7. #endif  
  8.   
  9.     u32 rcv_isn; /* 客户端的初始序列号 */  
  10.     u32 snt_isn; /* 本端的初始序列号 */  
  11.     u32 snt_synack; /* synack sent time */  
  12. };  

 

操作函数

 

request_sock_ops为处理连接请求块的函数指针表,对于TCP,它的实例为tcp_request_sock_ops。

[java]  view plain copy
  1. struct request_sock_ops {  
  2.     int family; /* 所属的协议族 */  
  3.     int obj_size; /* 连接请求块的大小 */  
  4.     struct kmem_cache *slab; /* 连接请求块的高速缓存 */  
  5.     char *slab_name;  
  6.   
  7.     /* 重传SYNACK */  
  8.     int (*rtx_syn_ack) (struct sock *sk, struct request_sock *req, struct request_values *rvp);  
  9.   
  10.     /* 发送ACK */  
  11.     void (*send_ack) (struct sock *sk, struct sk_buff *skb, struct request_sock *req);  
  12.   
  13.     /* 发送RST */  
  14.     void (*send_reset) (struct sock *sk, struct sk_buff *skb);  
  15.   
  16.     /* 析构函数 */  
  17.     void (*destructor) (struct request_sock *req);   
  18.   
  19.     /* SYNACK超时处理函数 */  
  20.     void (*syn_ack_timeout) (struct sock *sk, struct request_sock *req);  
  21. };  

对于TCP,它的实例为tcp_request_sock_ops。

[java]  view plain copy
  1. struct request_sock_ops tcp_request_sock_ops__read_mostly = {  
  2.     .family = PF_INET,  
  3.     .obj_size = sizeof(struct tcp_request_sock),  
  4.   
  5.     .rtx_syn_ack = tcp_v4_rtx_synack,  
  6.     .send_ack = tcp_v4_reqsk_send_ack,  
  7.     .destructor = tcp_v4_reqsk_destructor,  
  8.     .send_reset = tcp_v4_send_reset,  
  9.     .syn_ack_timeout = tcp_syn_ack_timeout,  
  10. };  

 

建立连接时处理

 

(1)分配

从缓存块中分配一个request_sock实例,并指定此实例的操作函数集。

[java]  view plain copy
  1. static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops)  
  2. {  
  3.     struct request_sock *req = reqsk_alloc(ops); /* 分配一个连接请求块 */  
  4.     struct inet_request_sock *ireq = inet_rsk(req);  
  5.   
  6.     if (req != NULL) {  
  7.         kmemcheck_annotate_bitfield(ireq, flags);  
  8.         ireq->opt = NULL;  
  9.     }  
  10.   
  11.     return req;  
  12. }  
  13.    
  14. static inline struct request_sock *reqsk_alloc(const struct request_sock_ops *ops)  
  15. {  
  16.     struct request_sock *req = kmem_cache_alloc(ops->slab, GFP_ATOMIC); /* 从缓存块中分配 */  
  17.     if (req != NULL)  
  18.         req->rsk_ops = ops; /* 指定此连接请求块的操作函数集 */  
  19.     return req;  
  20. }  

 

(2)释放

释放一个request_sock实例。

[java]  view plain copy
  1. static inline void reqsk_free(struct request_sock *req)  
  2. {  
  3.     req->rsk_ops->destructor(req); /* 对于TCP是tcp_v4_reqsk_destructor() */  
  4.     __reqsk_free(req);  
  5. }  
  6.   
  7. /* IPv4 request_sock destructor. */  
  8. static void tcp_v4_reqsk_destructor(struct request_sock *req)  
  9. {  
  10.     kfree(inet_rsk(req)->opt); /* 释放IP选项 */  
  11. }  
  12.   
  13. static inline void __reqsk_free(struct request_sock *req)  
  14. {  
  15.     kmem_cache_free(req->rsk_ops->slab, req);  
  16. }  

 

(3)初始化

初始化连接请求块,包括request_sock、inet_request_sock、tcp_request_sock。

[java]  view plain copy
  1. static inline void tcp_openreq_init(struct request_sock *req,   
  2.                         struct tcp_options_received *rx_opt, struct sk_buff *skb)  
  3. {  
  4.     struct inet_request_sock *ireq = inet_rsk(req);  
  5.   
  6.     req->rcv_wnd = 0/* 本端的初始接收窗口大小 */  
  7.     req->cookie_ts = 0/* syncookie: encode tcpopts in timestamp */  
  8.     tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; /* 客户端的初始序列号 */  
  9.     req->mss = rx_opt->mss_clamp; /* 客户端通告的MSS */  
  10.     req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0/* 客户端SYN的时间戳 */  
  11.     ireq->tstamp_ok = rx_opt->tstamp_ok; /* 连接是否使用TIMESTAMP选项 */  
  12.     ireq->sack_ok = rx_opt->sack_ok; /* 连接是否使用SACK选项 */  
  13.     ireq->snd_wscale = rx_opt->snd_wscale; /* 对端的窗口扩大因子 */  
  14.     ireq->wscale_ok = rx_opt->wscale_ok; /* 连接是否使用Window Scaling选项 */  
  15.     ireq->acked = 0;  
  16.     ireq->ecn_ok = 0;  
  17.     ireq->rmt_port = tcp_hdr(skb)->source; /* 对端的端口 */  
  18.     ireq->loc_port = tcp_hdr(skb)->dest; /* 本端的端口 */  
  19. }  

 

(4)入队列

把连接请求块链入半连接队列中。

[java]  view plain copy
  1. void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, unsigned long timeout)  
  2. {  
  3.     struct inet_connection_sock *icsk = inet_csk(sk);  
  4.     struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; /* 半连接队列 */  
  5.   
  6.     /* 根据目的IP、目的端口和随机数,计算出该连接请求块的hash值 */  
  7.     const u32 h = inet_synq_hash(inet_rsk(req)->rmt_addr, inet_rsk(req)->rmt_port, lopt->hash_rnd,  
  8.                                  lopt->nr_table_entries);  
  9.   
  10.     /* 设置连接请求块的超时时间、按照hash值把它链入半连接队列 */  
  11.     reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout);  
  12.   
  13.     /* 更新半连接队列长度,如果之前为0,则启动定时器 */  
  14.     inet_csk_reqsk_queue_added(sk, timeout);  
  15. }  

设置连接请求块的超时时间、按照hash值把它链入半连接队列。

[java]  view plain copy
  1. static inline void reqsk_queue_hash_req(struct request_sock_queue *queue, u32 hash,  
  2.                            struct request_sock *req, unsigned long timeout)  
  3. {  
  4.     struct listen_sock *lopt = queue->listen_opt; /* 半连接队列 */  
  5.     req->expires = jiffies + timeout; /* SYNACK的超时时间 */  
  6.     req->retrans = 0/* SYNACK的重传次数 */  
  7.     req->sk = NULL; /* 连接尚未建立 */  
  8.     req->dl_next = lopt->syn_table[hash]; /* 指向下一个req */  
  9.   
  10.     write_lock(&queue->syn_wait_lock);  
  11.     lopt->syn_table[hash] = req; /* 把请求链入半连接队列 */  
  12.     write_unlock(&queue->syn_wait_lock);  
  13. }  
[java]  view plain copy
  1. static inline void inet_csk_reqsk_queue_added(struct sock *sk, const unsigned long timeout)  
  2. {  
  3.     /* 更新半连接队列长度,如果之前的长度为0 */  
  4.     if (reqsk_queue_added(&inet_csk(sk)->icsk_accept_queue) == 0)  
  5.         inet_csk_reset_keepalive_timer(sk, timeout); /*启动定时器 */  
  6. }  
  7.   
  8. static inline int reqsk_queue_added(struct request_sock_queue *queue)  
  9. {  
  10.     struct listen_opt *lopt = queue->listen_opt; /* 半连接队列 */  
  11.   
  12.     const int prev_qlen = lopt->qlen; /* 之前的半连接队列长度 */  
  13.     lopt->qlen_young++;  /* 更新未重传过的请求块数 */  
  14.     lopt->qlen++; /* 更新半连接队列长度 */  
  15.     return prev_qlen;  
  16. }  
  17.   
  18. void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long len)  
  19. {  
  20.     sk_reset_timer(sk, &sk->sk_timer, jiffies + len);  
  21. }  

根据目的IP、目的端口和随机数,计算出该连接请求块的hash值。

[java]  view plain copy
  1. static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd,   
  2.                                  const u32 synq_hsize)  
  3. {  
  4.     return jhash_2words((__force u32) raddr, (__force u32) rport, rnd) & (synq_hsize - 1);  
  5. }  
  6.   
  7. static inline u32 jhash_2words(u32 a, u32 b, u32 initval)  
  8. {  
  9.     return jhash_3words(a, b, 0, initval);  
  10. }  
  11.   
  12. static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)  
  13. {  
  14.     a += JHASH_INITVAL;  
  15.     b += JHASH_INITVAL;  
  16.     c += initval;  
  17.   
  18.     __jhash_final(a, b, c);  
  19.     return c;  
  20. }  
  21.   
  22. /* An arbitrary initial parameter */  
  23. #define JHASH_INITVAL 0xdeadbeef  
  24.   
  25. /* __jhash_final - final mixing of 3 32-bit values (a, b, c) into c */  
  26. #define __jhash_final(a, b, c)    \  
  27. {  
  28.     c ^= b; c -= rol32(b, 14);    \  
  29.     a ^= c; a -= rol32(c, 11);    \  
  30.     b ^= a; b -= rol32(a, 25);    \  
  31.     c ^= b; c -= rol32(b, 16);    \  
  32.     a ^= c; a -= rol32(c, 4);    \  
  33.     b ^= a; b -= rol32(a, 14);    \  
  34.     c ^= b; c -= rol32(b, 24);    \  
  35. }  
  36.    
  37. /** 
  38.  * rol32 - rotate a 32-bit value left 
  39.  * @word: value to rotate 
  40.  * @shift: bits to roll 
  41.  */  
  42. static inline __u32 rol32(__u32 word, unsigned int shift)  
  43. {  
  44.     return (word << shift) | (word >> (32 - shift));  
  45. }  


(5) 出队列

把连接请求块从半连接队列中删除。 

[java]  view plain copy
  1. static inline void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req,  
  2.     struct request_sock **prev)  
  3. {  
  4.     inet_csk_reqsk_queue_unlink(sk, req, prev); /* 把连接请求块从半连接队列中删除 */  
  5.     inet_csk_reqsk_queue_removed(sk, req); /* 更新半连接队列长度,如果为0,则删除定时器 */  
  6.     reqsk_free(req); /* 释放连接请求块 */  
  7. }  
  8.   
  9. static inline void inet_csk_reqsk_queue_unlink(struct sock *sk, struct request_sock *req,  
  10.     struct request_sock **prev)  
  11. {  
  12.     reqsk_queue_unlink(&inet_csk(sk)->icsk_accept_queue, req, prev);  
  13. }  

把连接请求块从半连接队列中删除。

[java]  view plain copy
  1. static inline void reqsk_queue_unlink(struct request_sock_queue *queue, struct request_sock *req,  
  2.     struct request_sock **prev_req)  
  3. {  
  4.     write_lock(&queue->syn_wait_lock);  
  5.     *prev_req = req->dl_next; /* 改变了指针的值,相当于删除了req指向的实例 */  
  6.     write_unlock(&queue->syn_wait_lock);  
  7. }  
  8.   
  9. static inline void inet_csk_reqsk_queue_removed(struct sock *sk, struct request_sock *req)  
  10. {  
  11.     /* 如果半连接队列长度为0,则删除定时器 */  
  12.     if (reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req) == 0)  
  13.         inet_csk_delete_keepalive_timer(sk);  
  14. }  

更新未重传过的连接请求块数、更新半连接队列长度。

[java]  view plain copy
  1. static inline int reqsk_queue_removed(struct request_sock_queue *queue, struct request_sock *req)  
  2. {  
  3.     struct listen_sock *lopt = queue->listen_opt;  
  4.     if (req->retrans == 0)  
  5.         --lopt->qlen_yong;  
  6.     return --lopt->qlen;  
  7. }  

你可能感兴趣的:(计算机网络)