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


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

    内核版本:3.6

    Author:zhangskd @ csdn blog

    存储队列

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

    /**
     * @icsk_accept_queue: FIFO of established children
     */
    struct inet_connection_sock {
        ...
        /* 存放SYN_RECV、ESTABLISHED状态的连接请求块tcp_request_sock */
        struct request_sock_queue icsk_accept_queue;
        ...
    };
    
    /* struct request_sock_queue - queue of request_sock
     * @rskq_accept_head: FIFO head of established children
     * @rskq_accept_tail: FIFO tail of established children
     * @syn_wait_lock: serializer
     * @rskq_defer_accept: User waits for some data after accept()
     */
    
    struct request_sock_queue {
        struct request_sock *rskq_accept_head; /* ESTABLISHED状态请求块的头,等待accept */
        struct request_sock *rskq_accept_tail; /* ESTABLISHED状态请求块的尾 */
    
        rwlock_t syn_wait_lock; /* listen_opt及其成员的读写锁 */
        u8 rskq_defer_accept;
    
        /* 3 byte hole, try to pack */
        struct listen_sock *listen_opt; /* SYN_RECV状态的请求块,等待ACK */
    };
    

    连接请求块(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()时创建。

    /* struct listen_sock - listen state
     *
     * @max_qlen_log - log_2 of maximal queued SYNs/REQUSTs
     */
    
    struct listen_sock {
        u8 max_qlen_log; /* 半连接队列最大长度的log2 */
        u8 synflood_warned; /* SYN Flood标志 */
        /* 2 bytes hole, try to use */
        int qlen; /* 当前连接请求块的数目 */
        int qlen_young; /* 当前未重传过SYNACK的请求块数目 */
        int clock_hand;
        u32 hash_rnd; /* 一个随机数,用于计算hash值 */
        u32 nr_table_entries; /* 半连接队列最大长度 */
        struct request_sock *syn_table[0]; /* 连接请求块的指针数组 */
    };
    

    连接请求块

    最基本表示。

    /* struct request_sock - mini sock to represent a connection request */
    struct request_sock {
        struct request_sock *dl_next; /* Must be first member! */
    
        u16 mss; /* 客户端通告的MSS */
        u8 retrans; /* SYNACK的重传次数 */
        u8 cookie_ts; /* syncookie: encode tcpopts in timestamp */
        u32 window_clamp; /* 本端的最大通告窗口 */
        u32 rcv_wnd; /* 本端的接收窗口大小 */
        u32 ts_recent; /* 下个发送段的时间戳回显值 */
        unsigned long expires; /* SYNACK的超时时间 */
        const struct request_sock_ops *rsk_ops; /* 指向tcp_request_sock_ops,操作函数 */
        struct sock *sk; /* 连接建立之前无效 */
        u32 secid;
        u32 peer_secid;
    };

    inet层表示。

    struct inet_request_sock {
        struct request_sock req;
    
    #if IS_ENABLED(CONFIG_IPV6)
        u16 inet6_rsk_offset;
    #endif
    
        __be16 loc_port; /* 本端端口 */
        __be32 loc_addr; /* 本端IP */
        __be32 rmt_addr; /* 客户端IP */
        __be16 rmt_port; /* 客户端端口 */
    
        kmemcheck_bitfield_begin(flags);
        u16 snd_wscale : 4, /* 对端的扩大因子 */
            rcv_wscale : 4, /* 本端的扩大因子 */
            tstamp_ok : 1, /* 连接是否支持TIMESTAMP选项 */
            sack_ok : 1, /* 连接是否支持SACK选项 */
            wscale_ok : 1, /* 连接是否支持Window Scale选项 */
            ecn_ok : 1, /* 连接是否支持ECN选项 */
            acked : 1,
            no_srccheck : 1;
        kmemcheck_bitfield_end(flags);
    
        struct ip_options_rcu *opt; /* IP选项 */
    };

    TCP层表示为tcp_request_sock。

    struct tcp_request_sock {
        struct inet_request_sock req;
    
    #ifdef CONFIG_TCP_MD5SIG
        /* Only used by TCP MD5 Signature so far. */
        const struct tcp_request_sock_ops *af_specific;
    #endif
    
        u32 rcv_isn; /* 客户端的初始序列号 */
        u32 snt_isn; /* 本端的初始序列号 */
        u32 snt_synack; /* synack sent time */
    };
    

    操作函数

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

    struct request_sock_ops {
        int family; /* 所属的协议族 */
        int obj_size; /* 连接请求块的大小 */
        struct kmem_cache *slab; /* 连接请求块的高速缓存 */
        char *slab_name;
    
        /* 重传SYNACK */
        int (*rtx_syn_ack) (struct sock *sk, struct request_sock *req, struct request_values *rvp);
    
        /* 发送ACK */
        void (*send_ack) (struct sock *sk, struct sk_buff *skb, struct request_sock *req);
    
        /* 发送RST */
        void (*send_reset) (struct sock *sk, struct sk_buff *skb);
    
        /* 析构函数 */
        void (*destructor) (struct request_sock *req); 
    
        /* SYNACK超时处理函数 */
        void (*syn_ack_timeout) (struct sock *sk, struct request_sock *req);
    };
    

    对于TCP,它的实例为tcp_request_sock_ops。

    struct request_sock_ops tcp_request_sock_ops__read_mostly = {
        .family = PF_INET,
        .obj_size = sizeof(struct tcp_request_sock),
    
        .rtx_syn_ack = tcp_v4_rtx_synack,
        .send_ack = tcp_v4_reqsk_send_ack,
        .destructor = tcp_v4_reqsk_destructor,
        .send_reset = tcp_v4_send_reset,
        .syn_ack_timeout = tcp_syn_ack_timeout,
    };
    

    建立连接时处理

    (1)分配

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

    static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops)
    {
        struct request_sock *req = reqsk_alloc(ops); /* 分配一个连接请求块 */
        struct inet_request_sock *ireq = inet_rsk(req);
    
        if (req != NULL) {
            kmemcheck_annotate_bitfield(ireq, flags);
            ireq->opt = NULL;
        }
    
        return req;
    }
     
    static inline struct request_sock *reqsk_alloc(const struct request_sock_ops *ops)
    {
        struct request_sock *req = kmem_cache_alloc(ops->slab, GFP_ATOMIC); /* 从缓存块中分配 */
        if (req != NULL)
            req->rsk_ops = ops; /* 指定此连接请求块的操作函数集 */
        return req;
    }
    

    (2)释放

    释放一个request_sock实例。

    static inline void reqsk_free(struct request_sock *req)
    {
        req->rsk_ops->destructor(req); /* 对于TCP是tcp_v4_reqsk_destructor() */
        __reqsk_free(req);
    }
    
    /* IPv4 request_sock destructor. */
    static void tcp_v4_reqsk_destructor(struct request_sock *req)
    {
        kfree(inet_rsk(req)->opt); /* 释放IP选项 */
    }
    
    static inline void __reqsk_free(struct request_sock *req)
    {
        kmem_cache_free(req->rsk_ops->slab, req);
    }
    

    (3)初始化

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

    static inline void tcp_openreq_init(struct request_sock *req, 
                            struct tcp_options_received *rx_opt, struct sk_buff *skb)
    {
        struct inet_request_sock *ireq = inet_rsk(req);
    
        req->rcv_wnd = 0; /* 本端的初始接收窗口大小 */
        req->cookie_ts = 0; /* syncookie: encode tcpopts in timestamp */
        tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq; /* 客户端的初始序列号 */
        req->mss = rx_opt->mss_clamp; /* 客户端通告的MSS */
        req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0; /* 客户端SYN的时间戳 */
        ireq->tstamp_ok = rx_opt->tstamp_ok; /* 连接是否使用TIMESTAMP选项 */
        ireq->sack_ok = rx_opt->sack_ok; /* 连接是否使用SACK选项 */
        ireq->snd_wscale = rx_opt->snd_wscale; /* 对端的窗口扩大因子 */
        ireq->wscale_ok = rx_opt->wscale_ok; /* 连接是否使用Window Scaling选项 */
        ireq->acked = 0;
        ireq->ecn_ok = 0;
        ireq->rmt_port = tcp_hdr(skb)->source; /* 对端的端口 */
        ireq->loc_port = tcp_hdr(skb)->dest; /* 本端的端口 */
    }

    (4)入队列

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

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

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

    static inline void reqsk_queue_hash_req(struct request_sock_queue *queue, u32 hash,
                               struct request_sock *req, unsigned long timeout)
    {
        struct listen_sock *lopt = queue->listen_opt; /* 半连接队列 */
        req->expires = jiffies + timeout; /* SYNACK的超时时间 */
        req->retrans = 0; /* SYNACK的重传次数 */
        req->sk = NULL; /* 连接尚未建立 */
        req->dl_next = lopt->syn_table[hash]; /* 指向下一个req */
    
        write_lock(&queue->syn_wait_lock);
        lopt->syn_table[hash] = req; /* 把请求链入半连接队列 */
        write_unlock(&queue->syn_wait_lock);
    }
    
    static inline void inet_csk_reqsk_queue_added(struct sock *sk, const unsigned long timeout)
    {
        /* 更新半连接队列长度,如果之前的长度为0 */
        if (reqsk_queue_added(&inet_csk(sk)->icsk_accept_queue) == 0)
            inet_csk_reset_keepalive_timer(sk, timeout); /*启动定时器 */
    }
    
    static inline int reqsk_queue_added(struct request_sock_queue *queue)
    {
        struct listen_opt *lopt = queue->listen_opt; /* 半连接队列 */
    
        const int prev_qlen = lopt->qlen; /* 之前的半连接队列长度 */
        lopt->qlen_young++;  /* 更新未重传过的请求块数 */
        lopt->qlen++; /* 更新半连接队列长度 */
        return prev_qlen;
    }
    
    void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long len)
    {
        sk_reset_timer(sk, &sk->sk_timer, jiffies + len);
    }

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

    static inline u32 inet_synq_hash(const __be32 raddr, const __be16 rport, const u32 rnd, 
                                     const u32 synq_hsize)
    {
        return jhash_2words((__force u32) raddr, (__force u32) rport, rnd) & (synq_hsize - 1);
    }
    
    static inline u32 jhash_2words(u32 a, u32 b, u32 initval)
    {
        return jhash_3words(a, b, 0, initval);
    }
    
    static inline u32 jhash_3words(u32 a, u32 b, u32 c, u32 initval)
    {
        a += JHASH_INITVAL;
        b += JHASH_INITVAL;
        c += initval;
    
        __jhash_final(a, b, c);
        return c;
    }
    
    /* An arbitrary initial parameter */
    #define JHASH_INITVAL 0xdeadbeef
    
    /* __jhash_final - final mixing of 3 32-bit values (a, b, c) into c */
    #define __jhash_final(a, b, c)    
    {
        c ^= b; c -= rol32(b, 14);    
        a ^= c; a -= rol32(c, 11);    
        b ^= a; b -= rol32(a, 25);    
        c ^= b; c -= rol32(b, 16);    
        a ^= c; a -= rol32(c, 4);    
        b ^= a; b -= rol32(a, 14);    
        c ^= b; c -= rol32(b, 24);    
    }
     
    /**
     * rol32 - rotate a 32-bit value left
     * @word: value to rotate
     * @shift: bits to roll
     */
    static inline __u32 rol32(__u32 word, unsigned int shift)
    {
        return (word << shift) | (word >> (32 - shift));
    }
    


    (5) 出队列

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

    static inline void inet_csk_reqsk_queue_drop(struct sock *sk, struct request_sock *req,
        struct request_sock **prev)
    {
        inet_csk_reqsk_queue_unlink(sk, req, prev); /* 把连接请求块从半连接队列中删除 */
        inet_csk_reqsk_queue_removed(sk, req); /* 更新半连接队列长度,如果为0,则删除定时器 */
        reqsk_free(req); /* 释放连接请求块 */
    }
    
    static inline void inet_csk_reqsk_queue_unlink(struct sock *sk, struct request_sock *req,
        struct request_sock **prev)
    {
        reqsk_queue_unlink(&inet_csk(sk)->icsk_accept_queue, req, prev);
    }
    

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

    static inline void reqsk_queue_unlink(struct request_sock_queue *queue, struct request_sock *req,
        struct request_sock **prev_req)
    {
        write_lock(&queue->syn_wait_lock);
        *prev_req = req->dl_next; /* 改变了指针的值,相当于删除了req指向的实例 */
        write_unlock(&queue->syn_wait_lock);
    }
    
    static inline void inet_csk_reqsk_queue_removed(struct sock *sk, struct request_sock *req)
    {
        /* 如果半连接队列长度为0,则删除定时器 */
        if (reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req) == 0)
            inet_csk_delete_keepalive_timer(sk);
    }
    

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

    static inline int reqsk_queue_removed(struct request_sock_queue *queue, struct request_sock *req)
    {
        struct listen_sock *lopt = queue->listen_opt;
        if (req->retrans == 0)
            --lopt->qlen_yong;
        return --lopt->qlen;
    }


     

  • 相关阅读:
    random 模块
    re 模块
    正则表达式
    15. 3Sum
    253. Meeting Rooms II
    91. Decode Ways
    17. Letter Combinations of a Phone Number
    314. Binary Tree Vertical Order Traversal
    311. Sparse Matrix Multiplication
    311. Sparse Matrix Multiplication
  • 原文地址:https://www.cnblogs.com/aiwz/p/6333310.html
Copyright © 2020-2023  润新知