• (2) linux 3.x



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


    本文主要分析:服务器端如何构造和发送SYNACK段。

    内核版本:3.6

    Author:zhangskd @ csdn blog

    发送入口

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

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

    然后调用ip_build_and_send_pkt()添加IP报头后发送出去。

    1. /* Send a SYN-ACK after having received a SYN. 
    2.  * This still operates on a request_sock only, not on a big socket. 
    3.  */  
    4.   
    5. static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req,  
    6.      struct request_values *rvp, u16 queue_mapping, bool nocache)  
    7. {  
    8.     const struct inet_request_sock *ireq = inet_rsk(req);  
    9.     struct flowi4 fl4;  
    10.     int err = -1;  
    11.     struct sk_buff *skb;  
    12.   
    13.     /* First, grab a route. 
    14.      * 获取路由缓存。 
    15.      */  
    16.     if (! dst && (dst = inet_csk_route_req(sk, &fl4, req)) == NULL)  
    17.         return -1;  
    18.   
    19.     /* 构造一个SYNACK段,初始化TCP首部和skb中的一些字段。*/  
    20.     skb = tcp_make_synack(sk, dst, req, rvp);  
    21.   
    22.     if (skb) {  
    23.         /* 计算TCP报文的校验和 */  
    24.         __tcp_v4_send_check(skb, ireq->loc_addr, ireq->rmt_addr);  
    25.         skb_set_queue_mapping(skb, queue_mapping);  
    26.   
    27.         /* 添加IP报头,并把此SYNACK段发送出去 */  
    28.         err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr, ireq->rmt_addr, ireq->opt);  
    29.         err = net_xmit_eval(err);  
    30.     }  
    31.   
    32.     return err;  
    33. }  

    构造SYNACK段

    构造一个SYNACK段,初始化TCP报头和skb中的一些字段。

    1. /** 
    2.  * tcp_make_synack - Prepare a SYN-ACK. 
    3.  * sk: listener socket 
    4.  * dst: dst entry attached to the SYNACK 
    5.  * req: request_sock pointer 
    6.  * rvp: request_values pointer 
    7.  * 
    8.  * Allocate one skb and build a SYNACK packet. 
    9.  * @dst is consumed: Caller should not use it again. 
    10.  */  
    11.   
    12. struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req,  
    13.      struct request_values *rvp)  
    14. {  
    15.     struct tcp_out_options opts;  
    16.     struct tcp_extend_values *xvp = tcp_xv(rvp);  
    17.     struct inet_request_sock *ireq = inet_rsk(req);  
    18.     struct tcp_sock *tp = tcp_sk(sk);  
    19.     const struct tcp_cookie_values *cvp = tp->cookie_values;  
    20.     struct tcphdr *th;  
    21.     struct sk_buff *skb;  
    22.     struct tcp_md5sig_key *md5;  
    23.     int tcp_header_size;  
    24.     int mss;  
    25.     int s_data_desired = 0;  
    26.   
    27.     if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired)  
    28.         s_data_desired = cvp->s_data_desired;  
    29.   
    30.     /* 申请一个skb,用于发送SYNACK */  
    31.     skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, sk_gfp_atomic(sk, GFP_ATOMIC));  
    32.     if (unlikely(! skb)) {  
    33.         dst_release(dst);  
    34.         return NULL;  
    35.     }  
    36.      
    37.     /* Reserve space for headers.  
    38.      * 拓展headroom,为MAC、IP、TCP协议头预留空间。 
    39.      */  
    40.     skb_reserve(skb, MAX_TCP_HEADER);  
    41.     skb_dst_set(skb, dst); /* 保存路由缓存的地址 */  
    42.   
    43.     /* 从路由缓存中获取本端的通告MSS */  
    44.     mss = dst_metric_advmss(dst);  
    45.     if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)  
    46.         mss = tp->rx_opt.user_mss; /* 如果用户使用了TCP_MAXSEG选项 */  
    47.   
    48.     /* ignored for retransmitted syns. 
    49.      * 设置接收窗口的初始值、窗口扩大因子、通告窗口的上限。 
    50.      */  
    51.     if (req->rcv_wnd == 0) {  
    52.         __u8 rcv_wscale;  
    53.         /* 最大的通告窗口 */  
    54.         req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);  
    55.   
    56.         /* 如果用户使用了SO_RCVBUF选项做限制 */  
    57.         if (sk->sk_userlocks & SOCK_RCVBUF_LOCK &&  
    58.              (req->window_clamp > tcp_full_space(sk) || req->window_clamp == 0))  
    59.             req->window_clamp = tcp_full_space(sk); /* 3/4 * sk->sk_rcvbuf */  
    60.   
    61.         /* 获取接收窗口的初始值、窗口扩大因子和接收窗口的上限 */  
    62.         tcp_select_initial_window(tcp_full_space(sk), mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),  
    63.             &req->rcv_wnd, &req->window_clamp, ireq->wscale_ok, &rcv_wscale, dst_metric(dst, RTAX_INITRWND));  
    64.         ireq->rcv_wscale = rcv_wscale;  
    65.     }  
    66.     memset(&opts, 0, sizeof(opts));  
    67.   
    68. #ifdef CONFIG_SYN_COOKIES  
    69.     /* 如果SYNACK段使用了SYN COOKIE,并且使用时间戳选项, 
    70.      * 则把TCP选项信息保存在SYNACK段中tsval的低6位。 
    71.      */  
    72.     if (unlikely(req->cookie_ts))  
    73.         TCP_SKB_CB(skb)->when = cookie_init_timestamp(req);  
    74.     else  
    75. #endif  
    76.     TCP_SKB_CB(skb)->when = tcp_time_stamp;  
    77.   
    78.     /* TCP首部和选项的长度,赋值TCP选项实例tcp_out_options */  
    79.     tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, &md5, xvp) + sizeof(*th);  
    80.   
    81.     /* 向headroom扩展,使data room包含TCP首部和选项 */  
    82.     skb_push(skb, tcp_header_size);  
    83.     skb_reset_transport_header(skb);  
    84.   
    85.     th = tcp_hdr(skb);  
    86.     memset(th, 0, sizeof(struct tcphdr));  
    87.     th->syn = 1;  
    88.     th->ack = 1;  
    89.     /* 如果连接支持ECN,把th->ece置1 */  
    90.     TCP_ECN_make_synack(req, th);  
    91.     th->source = ireq->loc_port; /* 源端口 */  
    92.     th->dest = ireq->rmt_port; /* 目的端口 */  
    93.     /* 初始化skb中的一些控制字段 */  
    94.     tcp_init_nondata_skb(skb, tcp_rsk(req)->snt_isn, TCPHDR_SYN | TCPHDR_ACK);  
    95.   
    96.     /* TCPCT,新内核已废弃此选项,不做分析 */  
    97.     if (OPTION_COOKIE_EXTENSION & opts.options) {  
    98.         ...  
    99.     }  
    100.   
    101.     th->seq = htonl(TCP_SKB_CB(skb)->seq); /* 序号 */  
    102.     th->ack_seq = htonl(tcp_rsk(seq)->rcv_isn + 1); /* 确认序号 */  
    103.     /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */  
    104.     th->window = htons(min(req->rcv_wnd, 65535U)); /* 初始接收窗口 */  
    105.   
    106.     /* 把TCP选项实例tcp_out_options写到skb中 */  
    107.     tcp_options_write((__u32 *) (th + 1), tp, &opts);  
    108.     th->doff = (tcp_header_size >> 2); /* TCP首部长度 */  
    109.     TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS, tcp_skb_pcount(skb));  
    110.   
    111. #ifdef CONFIG_TCP_MD5SIG  
    112.     /* Okay, we have all we need - do the md5 hash if needed */  
    113.     if (md5)  
    114.         tcp_rsk(req)->af_specific->calc_md5_hash(opts.hash_location, md5, NULL, req, skb);  
    115. #endif  
    116.   
    117.     return skb;  
    118. }  
    1. /* MAC层、IP层、TCP层,首部和选项的最大长度 */  
    2. #define MAX_TCP_HEADER (128 + MAX_HEADER)  
    3. #define MAX_TCP_OPTION_SPACE 40  
    4.   
    5. static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask)  
    6. {  
    7.     /* Allow access to emergency reserves */  
    8.     return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC);  
    9. }  

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

    中tsval的低6位。

    1. /* When syncookies are in effect and tcp timestamps are enabled we encode tcp options 
    2.  * in the lower bits of the timestamp value that will be sent in the syn-ack. 
    3.  * Since subsequent timestamps use the normal tcp_time_stamp value, we must make 
    4.  * sure that the resulting initial timestamp is <= tcp_time_stamp. 
    5.  */  
    6. __u32 cookie_init_timestamp(struct request_sock *req)  
    7. {  
    8.     struct inet_request_sock *ireq;  
    9.     u32 ts, ts_now = tcp_time_stamp;  
    10.     u32 options = 0;  
    11.     ireq = inet_rsk(req);  
    12.   
    13.     options = ireq->wscale_ok ? ireq->snd_wscale : 0xf;  
    14.     options |= ireq->sack_ok << 4;  
    15.     options |= ireq->ecn_ok << 5;  
    16.   
    17.     ts = ts_now & ~TSMASK;  
    18.     ts |= options;  
    19.   
    20.     if (ts > ts_now) {  
    21.         ts >>= TSBITS;  
    22.         ts--;  
    23.         ts <<= TSBITS;  
    24.         ts |= options;  
    25.     }  
    26.     return ts;  
    27. }  
    28.   
    29. #define TSBITS 6  
    30. #define TSMASK (((__u32) 1 << TSBITS) - 1)  

    TCP Cookie Transaction (TCPCT) 选项功能类似与SYN Cookie,是2009年加入内核,

    2013/3从内核中移除,本文不对其进行分析。

    1. #define OPTION_SACK_ADVERTISE (1 << 0)  
    2. #define OPTION_TS (1 << 1)  
    3. #define OPTION_MD5 (1 << 2)  
    4. #define OPTION_WSCALE (1 << 3)  
    5. #define OPTION_COOKIE_EXTENSION (1 << 4)  
    6. #define OPTION_FAST_OPEN_COOKIE (1 << 8)  
    7.   
    8. /* TCP选项实例,用于发送。*/  
    9. struct tcp_out_options {  
    10.     u16 options; /* bit field of OPTION_* */  
    11.     u16 mss; /* 0 to disable,Max Segment Size选项 */  
    12.     u8 ws; /* window scale, 0 to disable,Window Scaling选项 */  
    13.     u8 num_sack_blocks; /* number of SACK blocks to include */  
    14.     u8 hash_size; /* bytes in hash_location */  
    15.     __u8 *hash_location; /* temporary pointer, overloaded */  
    16.     __u32 tsval, tsecr; /* need to include OPTION_TS,时间戳 */  
    17.     struct tcp_fastopen_cookie *fastopen_cookie; /* Fast open cookie,Fast Open选项 */  
    18. };  

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

    1. /* Set up TCP options for SYN-ACKs. */  
    2. static unsigned int tcp_synack_options(struct sock *sk, struct request_sock *req,  
    3.     unsigned int mss, struct sk_buff *skb, struct tcp_out_options *opts,  
    4.     struct tcp_md5sig_key **md5, struct tcp_extend_values *xvp)  
    5. {  
    6.     struct inet_request_sock *ireq = inet_rsk(req);  
    7.     unsigned int remaining = MAX_TCP_OPTION_SPACE;  
    8.     u8 cookie_plus = (xvp != NULL && ! xvp->cookie_out_never) ? xvp->cookie_plus : 0;  
    9.   
    10. #define CONFIG_TCP_MD5SIG  
    11.     *md5 = tcp_rsk(req)->af_specific->md5_lookup(sk, req);  
    12.     if (*md5) {  
    13.         opts->options |= OPTION_MD5;  
    14.         remaining -= TCPOLEN_MD5SIG_ALIGNED;  
    15.          
    16.         /* We can't fit any SACK blocks in a packet with MD5 + TS options. 
    17.          * There was discussion about disabling SACK rather than TS in order to 
    18.          * fit in better with old, buggy kernels, but that was deemed to be unnecessary. 
    19.          */  
    20.         ireq->tstamp_ok &= ! ireq->sack_ok;  
    21.     }  
    22. #else  
    23.     *md5 = NULL;  
    24. #endif  
    25.   
    26.     /* We always send an MSS option. */  
    27.     opt->mss = mss; /* Max Segment Size选项 */  
    28.     remaining -= TCPOLEN_MSS_ALIGNED;  
    29.   
    30.     if (likely(ireq->wscale_ok)) { /* Window Scaling选项 */  
    31.         opts->ws = ireq->rcv_wscale;  
    32.         opts->options |= OPTION_WSCALE;  
    33.         remaining -= TCPOLEN_WSCALE_ALIGNED;  
    34.     }  
    35.   
    36.     if (likely(ireq->tstamp_ok)) { /* 时间戳选项 */  
    37.         opts->options |= OPTION_TS;  
    38.         opts->tsval = TCP_SKB_CB(skb)->when;  
    39.         opts->tsecr = req->ts_recent;  
    40.         remaining -= TCPOLEN_TSTAMP_ALIGNED;  
    41.     }  
    42.   
    43.     if (likely(ireq->sack_ok)) { /* SACK Permit选项 */  
    44.         opts->options |= OPTION_SACK_ADVERTISE;  
    45.         if (unlikely(! ireq->tstamp_ok))  
    46.             remaining -= TCPOLEN_SACKPERM_ALIGNED;  
    47.     }  
    48.   
    49.     /* TCPCT选项,新内核以废弃 */  
    50.     if (*md5 == NULL && ireq->tstamp_ok && cookie_plus > TCPOLEN_COOKIE_BASE) {  
    51.         ...  
    52.     }  
    53.   
    54.     return MAX_TCP_OPTION_SPACE - remaining; /* TCP选项长度 */  
    55. }  

    初始化不携带数据skb的一些控制字段。

    1. /* Constructs common control bits of non-data skb. If SYN/FIN is present, 
    2.  * auto increment end seqno. 
    3.  */  
    4.   
    5. static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags)  
    6. {  
    7.     skb->ip_summed = CHECKSUM_PARTIAL; /* 由硬件计算协议首部和数据的校验和。*/  
    8.     skb->csum = 0;  
    9.   
    10.     TCP_SKB_CB(skb)->tcp_flags = flags;  
    11.     TCP_SKB_CB(skb)->sacked = 0;  
    12.   
    13.     skb_shinfo(skb)->gso_segs = 1;  
    14.     skb_shinfo(skb)->gso_size = 0;  
    15.     skb_shinfo(skb)->gso_type = 0;  
    16.   
    17.     TCP_SKB_CB(skb)->seq = seq;  
    18.     if (flags & (TCPHDR_SYN | TCPHDR_FIN))  
    19.         seq++;  
    20.     TCP_SKB_CB(skb)->end_seq = seq;  
    21. }  

    发送到IP层

    TCP报头中的校验和字段还没赋值,用__tcp_v4_send_check()来计算。

    1. static void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr)  
    2. {  
    3.     struct tcphdr *th = tcp_hdr(skb);  
    4.   
    5.     if (skb->ip_summed == CHECKSUM_PARTIAL) {  
    6.         /* 只计算伪首部,TCP报头和TCP数据的累加由硬件完成 */  
    7.         th->check = ~tcp_v4_check(skb->len, saddr, daddr, 0);  
    8.         skb->csum_start = skb_transport_header(skb) - skb->head; /* 下次计算开始位置的偏移 */  
    9.         skb->csum_offset = offsetof(struct tcphdr, check); /* 校验和值在TCP首部的偏移 */  
    10.   
    11.     } else {  
    12.         /* tcp_v4_check()累加伪首部,获取最终的校验和。 
    13.          * csum_partial()累加TCP报头。 
    14.          * skb->csum是TCP数据部分的累加,这是在从用户空间复制时顺便累加的。 
    15.          */  
    16.         th->check = tcp_v4_check(skb->len, saddr, daddr, csum_partial(th, th->doff << 2, skb->csum));  
    17.     }  
    18. }  

    给skb添加一个IP报头,然后发送出去。

    1. /* Add an ip header to a skbuff and send it out. */  
    2. int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, __be32 saddr, __be32 daddr,  
    3.          struct ip_options_rcu *opt)  
    4. {  
    5.     struct inet_sock *inet = inet_sk(sk);  
    6.     struct rtable *rt = skb_rtable(skb);  
    7.     struct iphdr *iph;  
    8.   
    9.      /* 向headroom扩展,使data room包含IP首部和选项 */  
    10.     skb_push(skb, sizeof(struct iphdr) + (opt ? opt->opt.optlen : 0));  
    11.     skb_reset_network_header(skb);  
    12.   
    13.     iph = ip_hdr(skb);  
    14.     iph->version = 4;  
    15.     iph->ihl = 5;  
    16.     ip->tos = inet->tos;  
    17.   
    18.     if (ip_dont_fragment(sk, &rt->dst))  
    19.         iph->frag_off = htons(IP_DF); /* 设置不允许分片标志 */  
    20.     else  
    21.         iph->frag_off = 0;  
    22.   
    23.     iph->ttl = ip_select_ttl(inet, &rt->dst);  
    24.     iph->daddr = (opt && opt->opt.srr ? opt->opt.faddr : daddr);  
    25.     iph->saddr = saddr;  
    26.     iph->protocol = sk->sk_protocol;  
    27.     ip_select_ident(iph, &rt->dst, sk);  
    28.   
    29.     if (opt && opt->opt.optlen) {  
    30.         iph->ihl += opt->opt.optlen >> 2;  
    31.         ip_options_build(skb, &opt->opt, daddr, rt, 0);  
    32.     }  
    33.   
    34.     skb->priority = sk->sk_priority;  
    35.     skb->mark = sk->sk_mark;  
    36.   
    37.     /* Send it out. */  
    38.     return ip_local_out(skb); /* 位于NF_INET_LOCAT_OUT之前 */  
    39.   
    40. }  


     


  • 相关阅读:
    js---查找数组中的最大值(最小值),及相应的下标
    JS数组遍历的几种方法
    在 forEach 中使用 async/await 遇到的问题
    js 事件冒泡和事件捕获
    JS中dom0级事件和dom2级事件的区别介绍
    Vue集成Ueditor
    vue富文本编辑器 Vue-Quill-Editor
    Redis问题1---redis满了怎么办
    jQuery火箭图标返回顶部代码
    遇到的小问题
  • 原文地址:https://www.cnblogs.com/ztguang/p/12644866.html
Copyright © 2020-2023  润新知