• 握手4


    上一步分析到,服务器端将客户端发送的sk_buff,里面sock->sk_state 修改为 TCP_SYN_RECV,然后将该数据包发送给客户端。

    接下来,我们进一步分析,客户端接收到来自服务器端的tcp报文后,会发生什么。

    客户端处理该报文的顺序和服务器端处理该报文的顺序是一致的,为什么,因为他们都用的是TCP/IP 协议栈进行通信的。

    我们看一下 针对状态为 TCP_SYN_RECV 的 sock 的处理。客户端接收到的停tcp数据包中 ACK=1,SYN=1,我们看一下客户端的处理过程。

    
    

    static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
    const struct tcphdr *th, unsigned int len)
    {
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct tcp_sock *tp = tcp_sk(sk);
    struct tcp_fastopen_cookie foc = { .len = -1 };
    int saved_clamp = tp->rx_opt.mss_clamp;

    
    

    tcp_parse_options(skb, &tp->rx_opt, 0, &foc);
    if (tp->rx_opt.saw_tstamp)
    tp->rx_opt.rcv_tsecr -= tp->tsoffset;

    
    

    if (th->ack) {
    /* rfc793:
    * "If the state is SYN-SENT then
    * first check the ACK bit
    * If the ACK bit is set
    * If SEG.ACK =< ISS, or SEG.ACK > SND.NXT, send
    * a reset (unless the RST bit is set, if so drop
    * the segment and return)"
    */
    if (!after(TCP_SKB_CB(skb)->ack_seq, tp->snd_una) ||
    after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt))
    goto reset_and_undo;

    
    

    if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
    !between(tp->rx_opt.rcv_tsecr, tp->retrans_stamp,
    tcp_time_stamp)) {
    NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSACTIVEREJECTED);
    goto reset_and_undo;
    }

    
    

    /* Now ACK is acceptable.
    *
    * "If the RST bit is set
    * If the ACK was acceptable then signal the user "error:
    * connection reset", drop the segment, enter CLOSED state,
    * delete TCB, and return."
    */

    
    

    if (th->rst) {
    tcp_reset(sk);
    goto discard;
    }

    
    

    /* rfc793:
    * "fifth, if neither of the SYN or RST bits is set then
    * drop the segment and return."
    *
    * See note below!
    * --ANK(990513)
    */
    if (!th->syn)
    goto discard_and_undo;

    
    

    /* rfc793:
    * "If the SYN bit is on ...
    * are acceptable then ...
    * (our SYN has been ACKed), change the connection
    * state to ESTABLISHED..."
    */

    
    

    TCP_ECN_rcv_synack(tp, th);

    
    

    tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
    tcp_ack(sk, skb, FLAG_SLOWPATH);

    
    

    /* Ok.. it's good. Set up sequence numbers and
    * move to established.
    */
    tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
    tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;

    
    

    /* RFC1323: The window in SYN & SYN/ACK segments is
    * never scaled.
    */
    tp->snd_wnd = ntohs(th->window);

    
    

    if (!tp->rx_opt.wscale_ok) {
    tp->rx_opt.snd_wscale = tp->rx_opt.rcv_wscale = 0;
    tp->window_clamp = min(tp->window_clamp, 65535U);
    }

    
    

    if (tp->rx_opt.saw_tstamp) {
    tp->rx_opt.tstamp_ok = 1;
    tp->tcp_header_len =
    sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
    tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
    tcp_store_ts_recent(tp);
    } else {
    tp->tcp_header_len = sizeof(struct tcphdr);
    }

    
    

    if (tcp_is_sack(tp) && sysctl_tcp_fack)
    tcp_enable_fack(tp);

    
    

    tcp_mtup_init(sk);
    tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
    tcp_initialize_rcv_mss(sk);

    
    

    /* Remember, tcp_poll() does not lock socket!
    * Change state from SYN-SENT only after copied_seq
    * is initialized. */
    tp->copied_seq = tp->rcv_nxt;

    
    

    smp_mb();

    
    

    tcp_finish_connect(sk, skb);

    
    

    if ((tp->syn_fastopen || tp->syn_data) &&
    tcp_rcv_fastopen_synack(sk, skb, &foc))
    return -1;

    
    

    if (sk->sk_write_pending ||
    icsk->icsk_accept_queue.rskq_defer_accept ||
    icsk->icsk_ack.pingpong) {
    /* Save one ACK. Data will be ready after
    * several ticks, if write_pending is set.
    *
    * It may be deleted, but with this feature tcpdumps
    * look so _wonderfully_ clever, that I was not able
    * to stand against the temptation 8) --ANK
    */
    inet_csk_schedule_ack(sk);
    icsk->icsk_ack.lrcvtime = tcp_time_stamp;
    tcp_enter_quickack_mode(sk);
    inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
    TCP_DELACK_MAX, TCP_RTO_MAX);

    
    

    discard:
    __kfree_skb(skb);
    return 0;
    } else {
    tcp_send_ack(sk);
    }
    return -1;
    }

    
    

    /* No ACK in the segment */

    
    

    if (th->rst) {
    /* rfc793:
    * "If the RST bit is set
    *
    * Otherwise (no ACK) drop the segment and return."
    */

    
    

    goto discard_and_undo;
    }

    
    

    /* PAWS check. */
    if (tp->rx_opt.ts_recent_stamp && tp->rx_opt.saw_tstamp &&
    tcp_paws_reject(&tp->rx_opt, 0))
    goto discard_and_undo;

    
    

    if (th->syn) {
    /* We see SYN without ACK. It is attempt of
    * simultaneous connect with crossed SYNs.
    * Particularly, it can be connect to self.
    */
    tcp_set_state(sk, TCP_SYN_RECV);

    
    

    if (tp->rx_opt.saw_tstamp) {
    tp->rx_opt.tstamp_ok = 1;
    tcp_store_ts_recent(tp);
    tp->tcp_header_len =
    sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
    } else {
    tp->tcp_header_len = sizeof(struct tcphdr);
    }

    
    

    tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
    tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;

    
    

    /* RFC1323: The window in SYN & SYN/ACK segments is
    * never scaled.
    */
    tp->snd_wnd = ntohs(th->window);
    tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
    tp->max_window = tp->snd_wnd;

    
    

    TCP_ECN_rcv_syn(tp, th);

    
    

    tcp_mtup_init(sk);
    tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);
    tcp_initialize_rcv_mss(sk);

    
    

    tcp_send_synack(sk);
    #if 0
    /* Note, we could accept data and URG from this segment.
    * There are no obstacles to make this (except that we must
    * either change tcp_recvmsg() to prevent it from returning data
    * before 3WHS completes per RFC793, or employ TCP Fast Open).
    *
    * However, if we ignore data in ACKless segments sometimes,
    * we have no reasons to accept it sometimes.
    * Also, seems the code doing it in step6 of tcp_rcv_state_process
    * is not flawless. So, discard packet for sanity.
    * Uncomment this return to process the data.
    */
    return -1;
    #else
    goto discard;
    #endif
    }
    /* "fifth, if neither of the SYN or RST bits is set then
    * drop the segment and return."
    */

    
    

    discard_and_undo:
    tcp_clear_options(&tp->rx_opt);
    tp->rx_opt.mss_clamp = saved_clamp;
    goto discard;

    
    

    reset_and_undo:
    tcp_clear_options(&tp->rx_opt);
    tp->rx_opt.mss_clamp = saved_clamp;
    return 1;

    }

        
  • 相关阅读:
    Kubernetes学习之路(十)之资源清单定义
    Kubernetes学习之路(十一)之Pod状态和生命周期管理
    Kubernetes学习之路(七)之Coredns和Dashboard二进制部署
    Kubernetes学习之路(九)之kubernetes命令式快速创建应用
    Kubernetes学习之路(八)之Kubeadm部署集群
    Ceph学习之路(三)Ceph luminous版本部署
    Kubernetes学习之路(六)之创建K8S应用
    Redis学习之路(二)之Redis入门基础
    Redis学习之路(一)之缓存知识体系
    OpenStack入门篇(二十二)之实现阿里云VPC的SDN网络
  • 原文地址:https://www.cnblogs.com/guoyu1024/p/10592075.html
Copyright © 2020-2023  润新知