zoukankan      html  css  js  c++  java
  • tcp syn-synack-ack 服务端发送syn-ack

    tcp_v4_send_synack()用于发送SYNACK段,在tcp_v4_conn_request()中被调用。

    首先调用tcp_make_synack()构造SYNACK段,主要是构造TCP报头和初始化skb中的一些字段

    /*
     * 该结构主要描述双方的地址、所支持的TCP选项等
      tcp_request_sock包含inet_request_sock,inet_request_sock包含request_sock
     */
    struct inet_request_sock {
        struct request_sock    req;
        /*
         * 本地端口号  本地IP地址 对端IP地址
         */
    #define ir_loc_addr        req.__req_common.skc_rcv_saddr
    #define ir_rmt_addr        req.__req_common.skc_daddr
    #define ir_num            req.__req_common.skc_num
    #define ir_rmt_port        req.__req_common.skc_dport
    #define ir_v6_rmt_addr        req.__req_common.skc_v6_daddr
    #define ir_v6_loc_addr        req.__req_common.skc_v6_rcv_saddr
    #define ir_iif            req.__req_common.skc_bound_dev_if
    #define ir_cookie        req.__req_common.skc_cookie
    #define ireq_net        req.__req_common.skc_net
    #define ireq_state        req.__req_common.skc_state
    #define ireq_family        req.__req_common.skc_family
    /*
         * 发送窗口扩大因子,即要把TCP首部中指定的滑动窗口大小
         * 左移snd_wscale位后,作为真正的滑动窗口大小。在TCP
         * 首部中,滑动窗口大小值为16位的,而snd_wscale的值最大
         * 只能为14。所以,滑动窗口最大可被扩展到30位,在协议栈
         * 的实际实现中,可以看到窗口大小被置为5840,扩大因子为2,
         * 即实际的窗口大小为5840<<2=23360B
         */
        kmemcheck_bitfield_begin(flags);
        u16            snd_wscale : 4,
            /*
                     * 接收窗口扩大因子
                     */
                    rcv_wscale : 4,
                    /*
                     * 标识TCP段是否存在TCP时间戳选项
                     */
                    tstamp_ok  : 1,
                    /*
                     * 标识是否支持SACK,支持则该选项能出现在SYN段中
                     */
                    sack_ok       : 1,
                    /*
                     * 标识是否支持窗口扩大因子,如果支持该选项也只能出现
                     * 在SYN段中
                     */
                    wscale_ok  : 1,
                    /*
                     * 标志是否启用了显式拥塞通知
                     */
                    ecn_ok       : 1,
                    /*
                     * 标识已接收到第三次握手的ACK段,但是由于服务器繁忙
                     * 或其他原因导致未能建立起连接,此时可根据该标志重新
                     * 给客户端发送SYN+ACK段,再次进行连接的建立。该标志
                     * 的设置同时受sysctl_tcp_abort_on_overflow的控制
                     */
                    acked       : 1,
                    no_srccheck: 1;
        kmemcheck_bitfield_end(flags);
        u32                     ir_mark;
        //服务器端在接收到SYN后,会解析SKB中的ip选项字段,见tcp_v4_save_option
        union {
            struct ip_options_rcu    *opt;
            struct sk_buff        *pktopts;
        };
    };
    static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
        .mss_clamp    =    TCP_MSS_DEFAULT,
    #ifdef CONFIG_TCP_MD5SIG
        .req_md5_lookup    =    tcp_v4_md5_lookup,
        .calc_md5_hash    =    tcp_v4_md5_hash_skb,
    #endif
        .init_req    =    tcp_v4_init_req,
    #ifdef CONFIG_SYN_COOKIES
        .cookie_init_seq =    cookie_v4_init_sequence,
    #endif
        .route_req    =    tcp_v4_route_req,
        .init_seq    =    tcp_v4_init_sequence,
        .send_synack    =    tcp_v4_send_synack,
    };


    /*
    * 服务端用来处理客户端连接请求的函数 */ //服务器端收到SYN后,创建连接控制块request_sock。也就是收到第一步SYN的时候只是建立的连接控制块request_sock,当收到第三次ack的时候,才创建新的struct sock int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) { /* Never answer to SYNs send to broadcast or multicast */ if (skb_rtable(skb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) goto drop; return tcp_conn_request(&tcp_request_sock_ops, &tcp_request_sock_ipv4_ops, sk, skb); drop: tcp_listendrop(sk); return 0; }
    //服务器端收到SYN后,创建连接控制块request_sock
    /。也就是收到第一步SYN的时候只是建立的连接控制块request_sock
    ,当收到第三次ack的时候,才创建新的struct sock
    */
    int tcp_conn_request(struct request_sock_ops *rsk_ops,
                 const struct tcp_request_sock_ops *af_ops,
                 struct sock *sk, struct sk_buff *skb)
    {
        struct tcp_fastopen_cookie foc = { .len = -1 };
        __u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn;
        struct tcp_options_received tmp_opt;
        struct tcp_sock *tp = tcp_sk(sk);
        struct net *net = sock_net(sk);
        struct sock *fastopen_sk = NULL;
        struct dst_entry *dst = NULL;
        struct request_sock *req;
        bool want_cookie = false;
    /*如果启用了cookie机制,则会在第三步收到ACK的时候在tcp_v4_hnd_req中 
    的cookie_v4_check对之前发送的ack+syn进行检查,检查过程见cookie_v4_check
        */struct flowi fl;
    
        /* TW buckets are converted to open requests without
         * limitations, they conserve resources and peer is
         * evidently real one.
         */
          */
        /*
         * 如果SYN请求连接队列已满并且isn为零,则需做特别处理。
         * 这里的isn就是TCP_SKB_CB(skb)->when,而TCP_SKB_CB(skb)->when
         * 在TCP接收处理一开始就被清零,因此这里isn为零总是成立
         */
        if ((net->ipv4.sysctl_tcp_syncookies == 2 ||//sysctl_tcp_syncookies=2无条件生成syncookie
             inet_csk_reqsk_queue_is_full(sk)) && !isn) {//或者请求队列太长, 并且当前不是timewait
            want_cookie = tcp_syn_flood_action(sk, skb, rsk_ops->slab_name);//sysctl_tcp_syncookies>0, 并未当前socket打印一次告警
            if (!want_cookie)//队列满了,但不使用syncookie,则丢弃
                goto drop;
        }
    
    
        /* Accept backlog is full. If we have already queued enough
         * of warm entries in syn queue, drop request. It is better than
         * clogging syn queue with openreqs with exponentially increasing
         * timeout.
         */
         /*
         * 如果连接队列长度已达到上限且SYN请求队列中至少有一个握手过程中
         * 没有重传过的段,则丢弃当前连接请求.
         *  如果半连接队列中未重传的请求块数量大于1,
         * 则表示未来可能有2个完成的连接,这些新完成
         * 的连接要放到连接队列中,但此时连接队列已满
         * 。如果在接收到三次握手中最后的ACK后连接队列
         * 中没有空闲的位置,会忽略接收到的ACK包,连接
         * 建立会推迟,所以此时最好丢掉部分新的连接请
         * 求,空出资源以完成正在进行的连接建立过程。
         * 还要注意,这个判断并没有考虑半连接队列是否
         * 已满的问题。从这里可以看出,即使开启了
         * SYN cookies机制并不意味着一定可以完成连接的建立。
         * static inline int inet_csk_reqsk_queue_young(const struct sock *sk)
    {
        return reqsk_queue_len_young(&inet_csk(sk)->icsk_accept_queue);
    }static inline int reqsk_queue_len_young(const struct request_sock_queue *queue)
    {
        return atomic_read(&queue->young);
    }
    static inline bool sk_acceptq_is_full(const struct sock *sk)
    {
        return sk->sk_ack_backlog > sk->sk_max_ack_backlog;
    }
    */ if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) {//accept队列满,但是syn队列依然有可能被accept的连接,此时丢弃 NET_INC_STATS(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); goto drop; } /* * 可以接收并处理连接请求,调用inet_reqsk_alloc()分配一个连接请求 * 块,用于保存连接请求信息,同时初始化在建立连接过程中用来发送 * ACK、RST段的操作集合,以便在建立连接过程中能方便地调用这些接口 */ //rsk_ops ===tcp_request_sock_ops req = inet_reqsk_alloc(rsk_ops, sk, !want_cookie);//分配request_sock, 进入TCP_NEW_SYN_RECV状态 if (!req) goto drop; //af_ops====tcp_request_sock_ipv4_opss tcp_rsk(req)->af_specific = af_ops;//tcp_request_sock_ipv4_ops /* * 清除TCP选项后初始化mss_clamp和user_mss。 */ tcp_clear_options(&tmp_opt); tmp_opt.mss_clamp = af_ops->mss_clamp;//TCP_MSS_DEFAULT=536 tmp_opt.user_mss = tp->rx_opt.user_mss;//listen sock设置的或是tw的 /* * 解析SYN段中的TCP选项 */ tcp_parse_options(skb, &tmp_opt, 0, want_cookie ? NULL : &foc);//开启syncookie后则不用考虑fastopen, syncookie不允许使用tcp扩展 if (want_cookie && !tmp_opt.saw_tstamp) //开启syncookie,但是不带timestamp tcp_clear_options(&tmp_opt);//清除wscale,sack_ok等选项,因为没地方存 /* * 初始化该连接中是否启用时间戳的选项tstamp_ok */ tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; /* * 根据接收到SYN段中的选项和序号来初始化连接请求块信息 */ tcp_openreq_init(req, &tmp_opt, skb, sk); /* Note: tcp_v6_init_req() might override ir_iif for link locals */ inet_rsk(req)->ir_iif = inet_request_bound_dev_if(sk, skb); /* * 初始化TCP层次的连接请求信息块,包括目的地址、源地址, * 并调用tcp_v4_save_options从IP层私有控制块中获取IP * 选项保存到传输控制块的opt中,包括MSS、窗口扩大 * 因子、显式拥塞通知等 */ af_ops->init_req(req, sk, skb);//tcp_v4_init_req 会调用tcp_v4_save_options if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; if (!want_cookie && !isn) {//不需要生成syncookie,也不是从timewait recycle的新的sock /* VJ's idea. We save last timestamp seen * from the destination in peer table, when entering * state TIME-WAIT, and check against it before * accepting new connection request. * * If "isn" is not zero, this request hit alive * timewait bucket, so that all the necessary checks * are made in the function processing timewait state. */ /* * 进入TIMEWAIT状态时,从对端信息块中获取时间戳,在新的 * 连接请求之前检测PAWS */ if (tcp_death_row.sysctl_tw_recycle) { bool strict; dst = af_ops->route_req(sk, &fl, req, &strict); //tcp_v4_route_req //当起了快速回收tw_recycle的时候,这里可能有问题,可能连接建立不上,针对TCP时间戳PAWS漏洞的代码。 见:http://blog.chinaunix.net/uid-736168-id-376061.html //针对TCP时间戳PAWS漏洞,造成服务器端收到SYN的时候不回收SYN+ACK,解决办法是对方不要发送时间戳选项,同时关闭tcp_timestamps见tcp_v4_conn_request if (dst && strict && !tcp_peer_is_proven(req, dst, true, tmp_opt.saw_tstamp)) { NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); goto drop_and_release; /* 1 tcp的option有 time stamp字段. 2 tcp_tw_recycle有设置。 3 在路由表中是否存在完全相同的流(如果打开了xfrm的话, 还要比较端口,默认xfrm应该是打开的),如果存在则直接返回. 4 并且数据包的源地址和新请求的源地址相同. 5 根据路由表以及源地址能够查找到保存的peer (这个可以看我以前的blog,也就是保存了一些连接统计信息). 6 当前时间(接收到syn)比最后一次的时间(time stamp)小于60秒. 7 已经存在peer的最近一次时间戳要大于当前请求进来的时间戳. 从上面可以看到,上面的条件中1/2都是 server端可以控制的,而其他的条件, 都是很容易就满足的,因此我们举个例子。 如果客户端是NAT出来的,并且我们server端有打开tcp_tw_recycle , 并且time stamp也没有关闭,那么假设第一个连接进来,然后关闭,此时这个句柄处于time wait状态,然后很快(小于60秒)又一个客户端(相同的源地址,如果打开了xfrm还要相同的端口号)发一个syn包,此时linux内核就会认为这个数据包异常的,因此就会丢掉这个包,并发送rst。 而现在大部分的客户端都是NAT出来的,因此建议tw_recycle还 是关闭,或者说server段关闭掉time stamp(/proc/sys/net/ipv4/tcp_timestamps). */ } } /* Kill the following clause, if you dislike this way. */ //如果没开启sysctl_tw_recycle和syncookie,最后1/4的syn请求需要验证过去的连接信? /* * 未启动syncookies的情况下受到synflood攻击,则丢弃接收到的段 */ else if (!net->ipv4.sysctl_tcp_syncookies && (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < (sysctl_max_syn_backlog >> 2)) && !tcp_peer_is_proven(req, dst, false, tmp_opt.saw_tstamp)) {//如果不存在tcp metric或者过去的连接信息则丢弃 /* Without syncookies last quarter of * backlog is filled with destinations, * proven to be alive. * It means that we continue to communicate * to destinations, already remembered * to the moment of synflood. */ pr_drop_req(req, ntohs(tcp_hdr(skb)->source), rsk_ops->family); goto drop_and_release; } isn = af_ops->init_seq(skb);//tcp_v4_init_sequence,根据四元组,随机数,当前高精度时间来生成isn } if (!dst) { dst = af_ops->route_req(sk, &fl, req, NULL);//tcp_v4_route_req if (!dst) goto drop_and_free; } tcp_ecn_create_request(req, skb, sk, dst); if (want_cookie) { /* * 如果启动了syncookies,则每60秒警告一次可能受 * synflood攻击,同时由客户端IP地址、客户端端口、 * 服务器IP地址、服务器端口、客户端初始序列号 * 等要素经hash运算后加密得到服务端初始化序列号 */ //如果开启了syncookie选项,则需要检查收到的第三步ack和这个isn值是否一致 isn = cookie_init_sequence(af_ops, sk, skb, &req->mss); //cookie_v4_init_sequence生成syncookie,并作为ack的起始序号 req->cookie_ts = tmp_opt.tstamp_ok; if (!tmp_opt.tstamp_ok) inet_rsk(req)->ecn_ok = 0; } tcp_rsk(req)->snt_isn = isn; tcp_rsk(req)->txhash = net_tx_rndhash(); tcp_openreq_init_rwin(req, sk, dst);//设置初始化rwnd if (!want_cookie) { tcp_reqsk_record_syn(sk, req, skb);//如果设置保存TCP_SAVE_SYN标记,则保存 fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst); //验证后创建fastopen sock,并把数据部分放入接收队列中 } if (fastopen_sk) {//验证并创建fastsocket成功, 进入TCP_SYN_RCV状态 af_ops->send_synack(fastopen_sk, dst, &fl, req, &foc, TCP_SYNACK_FASTOPEN);//tcp_v4_send_synac /* Add the child socket directly into the accept queue */ inet_csk_reqsk_queue_add(sk, req, fastopen_sk);//添加到等待accept的队列 sk->sk_data_ready(sk); bh_unlock_sock(fastopen_sk); sock_put(fastopen_sk); } else { tcp_rsk(req)->tfo_listener = false; /* * 将连接请求块保存到其父传输控制块中的散列表中 */ if (!want_cookie) inet_csk_reqsk_queue_hash_add(sk, req, TCP_TIMEOUT_INIT);//插入ehash,并设置定时器 /* * 调用__tcp_v4_send_synack()组织并发送SYN+ACK段给客户端;如果 * 启用了syncookies,则是根据序号来判断三次握手的,因此无需保存 * 连接请求,直接将其释放 */ //如果是cookie,则真正的tcp_request_sock在第三步ack的时候在cookie_v4_check中创建 af_ops->send_synack(sk, dst, &fl, req, &foc, !want_cookie ? TCP_SYNACK_NORMAL : TCP_SYNACK_COOKIE);//tcp_v4_send_synack if (want_cookie) { reqsk_free(req);//启用syncookie的话,可以直接释放req return 0; } } reqsk_put(req); return 0; drop_and_release: dst_release(dst); drop_and_free: reqsk_free(req); drop: tcp_listendrop(sk); return 0;
    /*
     *    Send a SYN-ACK after having received a SYN.
     *    This still operates on a request_sock only, not on a big
     *    socket.
     */
     /*
    synack发送
    tcp_make_synack主要是根据需要设置synack的tcp选项, 使用syncookie的时候服务端不保存状态,会把tcp扩展项编码到timestamp中,把syncookie作为seq回传;
    对于fastopen请求,则会设置好fastopen cookie tcp选项回传,并对接收到的数据部分进行ack
    因为synack不用分片并且必须有路由缓存,直接调用ip_build_and_send_pkt()来发送
     */
    static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst,
                      struct flowi *fl,
                      struct request_sock *req,
                      struct tcp_fastopen_cookie *foc,
                      enum tcp_synack_type synack_type)
    {
        const struct inet_request_sock *ireq = inet_rsk(req);
        struct flowi4 fl4;
        int err = -1;
        struct sk_buff *skb;
    
        /* First, grab a route. */ /* 路由为空则查路由 */
        if (!dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)
            return -1;
     /* 构造syn+ack包 */
        skb = tcp_make_synack(sk, dst, req, foc, synack_type);
    
        if (skb) {/* 生成校验码 */
            __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr);
    
            err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr,
                            ireq->ir_rmt_addr,
                            ireq->opt);
            err = net_xmit_eval(err);
        }
    
        return err;
    }
    /**
     * tcp_make_synack - Prepare a SYN-ACK.
     * sk: listener socket
     * dst: dst entry attached to the SYNACK
     * req: request_sock pointer
     *
     * Allocate one skb and build a SYNACK packet.
     * @dst is consumed : Caller should not use it again.
     */
    struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
                    struct request_sock *req,
                    struct tcp_fastopen_cookie *foc,
                    enum tcp_synack_type synack_type)
    {
        struct inet_request_sock *ireq = inet_rsk(req);
        const struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_md5sig_key *md5 = NULL;
        struct tcp_out_options opts;
        struct sk_buff *skb;
        int tcp_header_size;
        struct tcphdr *th;
        u16 user_mss;
        int mss;
    /* 分配skb用于发送SYNACK **/
        skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC);
        if (unlikely(!skb)) {
            dst_release(dst);
            return NULL;
        }
        /* Reserve space for headers. */ /* 
        拓展headroom,为MAC、IP、TCP协议头预留空间
        保留头部空间 */
        skb_reserve(skb, MAX_TCP_HEADER);
        
        switch (synack_type) {
        case TCP_SYNACK_NORMAL:/* skb关联控制块 */
            skb_set_owner_w(skb, req_to_sk(req));
            break;
        case TCP_SYNACK_COOKIE:
            /* Under synflood, we do not attach skb to a socket,
             * to avoid false sharing.
             */
            break;
        case TCP_SYNACK_FASTOPEN:
            /* sk is a const pointer, because we want to express multiple
             * cpu might call us concurrently.
             * sk->sk_wmem_alloc in an atomic, we can promote to rw.
             */
            skb_set_owner_w(skb, (struct sock *)sk);
            break;
        }
        /* 保存路由缓存的地址 */
        skb_dst_set(skb, dst); /* 设置路由缓存 */
    /* 从路由缓存中获取本端的通告MSS */
        mss = dst_metric_advmss(dst); /* mss取从路由表中查询的mss与user_mss之间的较小值 */
        user_mss = READ_ONCE(tp->rx_opt.user_mss);
        if (user_mss && user_mss < mss)
            mss = user_mss;
    
        memset(&opts, 0, sizeof(opts));
    #ifdef CONFIG_SYN_COOKIES
        if (unlikely(req->cookie_ts))
            skb->skb_mstamp.stamp_jiffies = cookie_init_timestamp(req);
        else
    #endif
        skb_mstamp_get(&skb->skb_mstamp); /* 获取时间戳 */
    
    #ifdef CONFIG_TCP_MD5SIG
        rcu_read_lock();
        md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req));
    #endif
        skb_set_hash(skb, tcp_rsk(req)->txhash, PKT_HASH_TYPE_L4);
        tcp_header_size = tcp_synack_options(req, mss, skb, &opts, md5, foc) +
                  sizeof(*th);/* 设置tcp选项 */
    /* 构造填充tcp头 */
        skb_push(skb, tcp_header_size);
        skb_reset_transport_header(skb);
    
        th = (struct tcphdr *)skb->data;
        memset(th, 0, sizeof(struct tcphdr));
        th->syn = 1;
        th->ack = 1;
        tcp_ecn_make_synack(req, th);
        th->source = htons(ireq->ir_num);/* 源端口 */
        th->dest = ireq->ir_rmt_port;/* 目的端口 */
        /* Setting of flags are superfluous here for callers (and ECE is
         * not even correctly set)
         *//* 初始化skb中的一些控制字段 */
        tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn,
                     TCPHDR_SYN | TCPHDR_ACK);
    
        th->seq = htonl(TCP_SKB_CB(skb)->seq);
        /* XXX data is queued and acked as is. No buffer/window check */
        th->ack_seq = htonl(tcp_rsk(req)->rcv_nxt);
    
        /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */
        th->window = htons(min(req->rsk_rcv_wnd, 65535U)); /* 设置窗口 */
         /* 把TCP选项实例tcp_out_options写到skb中 */
        tcp_options_write((__be32 *)(th + 1), NULL, &opts);
        th->doff = (tcp_header_size >> 2); /* 设置首部长度 */
        __TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS);
    
    #ifdef CONFIG_TCP_MD5SIG
        /* Okay, we have all we need - do the md5 hash if needed */
        if (md5)
            tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location,
                               md5, req_to_sk(req), skb);
        rcu_read_unlock();
    #endif
    
        /* Do not fool tcpdump (if any), clean our debris */
        skb->tstamp.tv64 = 0;
        return skb;

    如果SYNACK段使用SYN Cookie,并且使用时间戳选项,则把TCP选项信息保存在SYNACK段中tsval的低6位

    /*
     * when syncookies are in effect and tcp timestamps are enabled we encode
     * tcp options in the lower bits of the timestamp value that will be
     * sent in the syn-ack.
     * Since subsequent timestamps use the normal tcp_time_stamp value, we
     * must make sure that the resulting initial timestamp is <= tcp_time_stamp.
     */
     /* There is no TS_OPT_TIMESTAMP:
     * if ACK contains timestamp option, we already know it was
     * requested/supported by the syn/synack exchange.
     */
    #define TSBITS    6
    #define TSMASK    (((__u32)1 << TSBITS) - 1)
    
    __u32 cookie_init_timestamp(struct request_sock *req)
    {
        struct inet_request_sock *ireq;
        u32 ts, ts_now = tcp_time_stamp;
        u32 options = 0;
    
        ireq = inet_rsk(req);
    
        options = ireq->wscale_ok ? ireq->snd_wscale : TS_OPT_WSCALE_MASK;
        if (ireq->sack_ok)
            options |= TS_OPT_SACK;
        if (ireq->ecn_ok)
            options |= TS_OPT_ECN;
    
        ts = ts_now & ~TSMASK;
        ts |= options;
        if (ts > ts_now) {
            ts >>= TSBITS;
            ts--;
            ts <<= TSBITS;
            ts |= options;
        }
        return ts;
    }

    赋值TCP选项实例tcp_out_options,用于构造SYNACK段。

    /* Set up TCP options for SYN-ACKs. */
    static unsigned int tcp_synack_options(struct request_sock *req,
                           unsigned int mss, struct sk_buff *skb,
                           struct tcp_out_options *opts,
                           const struct tcp_md5sig_key *md5,
                           struct tcp_fastopen_cookie *foc)
    {
        struct inet_request_sock *ireq = inet_rsk(req);
        unsigned int remaining = MAX_TCP_OPTION_SPACE;
    
    #ifdef CONFIG_TCP_MD5SIG
        if (md5) {
            opts->options |= OPTION_MD5;
            remaining -= TCPOLEN_MD5SIG_ALIGNED;
    
            /* We can't fit any SACK blocks in a packet with MD5 + TS
             * options. There was discussion about disabling SACK
             * rather than TS in order to fit in better with old,
             * buggy kernels, but that was deemed to be unnecessary.
             */
            ireq->tstamp_ok &= !ireq->sack_ok;
        }
    #endif
    
        /* We always send an MSS option. */
        opts->mss = mss;/* Max Segment Size选项 */
        remaining -= TCPOLEN_MSS_ALIGNED;
    
        if (likely(ireq->wscale_ok)) {/* Window Scaling选项 */
            opts->ws = ireq->rcv_wscale;
            opts->options |= OPTION_WSCALE;
            remaining -= TCPOLEN_WSCALE_ALIGNED;
        }
        if (likely(ireq->tstamp_ok)) {/* 时间戳选项 */
            opts->options |= OPTION_TS;
            opts->tsval = tcp_skb_timestamp(skb);
            opts->tsecr = req->ts_recent;
            remaining -= TCPOLEN_TSTAMP_ALIGNED;
        }
        if (likely(ireq->sack_ok)) {/* SACK Permit选项 */
            opts->options |= OPTION_SACK_ADVERTISE;
            if (unlikely(!ireq->tstamp_ok))
                remaining -= TCPOLEN_SACKPERM_ALIGNED;
        }
        if (foc != NULL && foc->len >= 0) {
            u32 need = foc->len;
    
            need += foc->exp ? TCPOLEN_EXP_FASTOPEN_BASE :
                       TCPOLEN_FASTOPEN_BASE;
            need = (need + 3) & ~3U;  /* Align to 32 bits */
            if (remaining >= need) {
                opts->options |= OPTION_FAST_OPEN_COOKIE;
                opts->fastopen_cookie = foc;
                remaining -= need;
            }
        }
    
        return MAX_TCP_OPTION_SPACE - remaining;/* TCP选项长度 */

     server端TCP在收到SYN请求后进行的处理总结如下:
    1、创建一个比较小的数据结构request_sock并保存连接信息
    2、将request_sock加入到syn table中,以便ACK到来时能够找到相应的连接信息
    3、创建SYN|ACK包并发送出去,同时设置重传定时器以避免SYN|ACK包丢失



  • 相关阅读:
    Android SDK、NDK、JNI的简单介绍
    深入理解计算机系统—异常
    Jmeter3.1 使用及新增报告功能
    jmeter3.1连接数据库报错,ORA00923: 未找到要求的 FROM 关键字
    Jenkins插件、war下载地址
    jenkins自动打tag
    jenkins参数化构建过程
    Jmeter接口测试自动化(jmeter+ant+jenkins持续集成)
    既然选择开始就不会停下
    知识提升整体
  • 原文地址:https://www.cnblogs.com/codestack/p/11147444.html
Copyright © 2011-2022 走看看