• (2) linux 3.x



    http://blog.csdn.net/zhangskd/article/details/16987637


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

    内核版本:3.6

    Author:zhangskd @ csdn blog

    存储队列

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

    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. };  
    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()时创建。

    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. };  

    连接请求块

    最基本表示。

    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层表示。

    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。

    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。

    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。

    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实例,并指定此实例的操作函数集。

    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实例。

    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。

    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)入队列

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

    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值把它链入半连接队列。

    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. }  
    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值。

    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) 出队列

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

    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. }  

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

    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. }  

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

    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. }  


  • 相关阅读:
    c++读写MySQL
    感叹游戏行业的飞速发展
    和真正的程序员在一起是怎样的体验
    程序媛是怎样找老公的
    IO和socket编程
    郁金香搜索引擎的方案
    实现一个自己的搜索引擎的初始规划
    JVM知识在离线数据中的运用
    看Lucene源码必须知道的基本规则和算法
    看Lucene源码必须知道的基本概念
  • 原文地址:https://www.cnblogs.com/ztguang/p/12644861.html
Copyright © 2020-2023  润新知