zoukankan      html  css  js  c++  java
  • 握手2

    服务器端在接收到一个sk_buff 以后,会调用函数 tcp_v4_rcv ,进入传输层处理

    在该处理函数中 会调用以下函数

    static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo,
                             struct sk_buff *skb,
                             const __be16 sport,
                             const __be16 dport)
    {
        struct sock *sk = skb_steal_sock(skb);
        const struct iphdr *iph = ip_hdr(skb);
    
        if (sk)
            return sk;
        else
            return __inet_lookup(dev_net(skb_dst(skb)->dev), hashinfo,
                         iph->saddr, sport,
                         iph->daddr, dport, inet_iif(skb));
    }

    其中 sk 为客户端的 sock ,真很好理解,在客户端填充 sk_buff 后,会给 sk_buff 的成员赋予本地的 sock 结构体。

    找到客户端的 sock 后。进入面函数

    tcp_v4_do_rcv

    int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
    {
        struct sock *rsk;
    
        if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
            struct dst_entry *dst = sk->sk_rx_dst;
    
            sock_rps_save_rxhash(sk, skb);
            sk_mark_napi_id(sk, skb);
            if (dst) {
                if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
                    dst->ops->check(dst, 0) == NULL) {
                    dst_release(dst);
                    sk->sk_rx_dst = NULL;
                }
            }
            tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len);
            return 0;
        }
    
        if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
            goto csum_err;
    
        if (sk->sk_state == TCP_LISTEN) {
            struct sock *nsk = tcp_v4_hnd_req(sk, skb);
            if (!nsk)
                goto discard;
    
            if (nsk != sk) {
                sock_rps_save_rxhash(nsk, skb);
                sk_mark_napi_id(sk, skb);
                if (tcp_child_process(sk, nsk, skb)) {
                    rsk = nsk;
                    goto reset;
                }
                return 0;
            }
        } else
            sock_rps_save_rxhash(sk, skb);
    
        if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
            rsk = sk;
            goto reset;
        }
        return 0;
    
    reset:
        tcp_v4_send_reset(rsk, skb);
    discard:
        kfree_skb(skb);
        /* Be careful here. If this function gets more complicated and
         * gcc suffers from register pressure on the x86, sk (in %ebx)
         * might be destroyed here. This current version compiles correctly,
         * but you have been warned.
         */
        return 0;
    
    csum_err:
        TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_CSUMERRORS);
        TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
        goto discard;
    }
    EXPORT_SYMBOL(tcp_v4_do_rcv);

    再继续调用函数

    int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                  const struct tcphdr *th, unsigned int len)
    {
        struct tcp_sock *tp = tcp_sk(sk);
        struct inet_connection_sock *icsk = inet_csk(sk);
        struct request_sock *req;
        int queued = 0;
        bool acceptable;
        u32 synack_stamp;
    
        tp->rx_opt.saw_tstamp = 0;
    
        switch (sk->sk_state) {
        case TCP_CLOSE:
            goto discard;
    
        case TCP_LISTEN:
            if (th->ack)
                return 1;
    
            if (th->rst)
                goto discard;
    
            if (th->syn) {
                if (th->fin)
                    goto discard;
                if (icsk->icsk_af_ops->conn_request(sk, skb) < 0)
                    return 1;
    
                /* Now we have several options: In theory there is
                 * nothing else in the frame. KA9Q has an option to
                 * send data with the syn, BSD accepts data with the
                 * syn up to the [to be] advertised window and
                 * Solaris 2.1 gives you a protocol error. For now
                 * we just ignore it, that fits the spec precisely
                 * and avoids incompatibilities. It would be nice in
                 * future to drop through and process the data.
                 *
                 * Now that TTCP is starting to be used we ought to
                 * queue this data.
                 * But, this leaves one open to an easy denial of
                 * service attack, and SYN cookies can't defend
                 * against this problem. So, we drop the data
                 * in the interest of security over speed unless
                 * it's still in use.
                 */
                kfree_skb(skb);
                return 0;
            }
            goto discard;
    
        case TCP_SYN_SENT:
            queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
            if (queued >= 0)
                return queued;
    
            /* Do step6 onward by hand. */
            tcp_urg(sk, skb, th);
            __kfree_skb(skb);
            tcp_data_snd_check(sk);
            return 0;
        }
    
        req = tp->fastopen_rsk;
        if (req != NULL) {
            WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV &&
                sk->sk_state != TCP_FIN_WAIT1);
    
            if (tcp_check_req(sk, skb, req, NULL, true) == NULL)
                goto discard;
        }
    
        if (!th->ack && !th->rst && !th->syn)
            goto discard;
    
        if (!tcp_validate_incoming(sk, skb, th, 0))
            return 0;
    
        /* step 5: check the ACK field */
        acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH |
                          FLAG_UPDATE_TS_RECENT) > 0;
    
        switch (sk->sk_state) {
        case TCP_SYN_RECV:
            if (!acceptable)
                return 1;
    
            /* Once we leave TCP_SYN_RECV, we no longer need req
             * so release it.
             */
            if (req) {
                synack_stamp = tcp_rsk(req)->snt_synack;
                tp->total_retrans = req->num_retrans;
                reqsk_fastopen_remove(sk, req, false);
            } else {
                synack_stamp = tp->lsndtime;
                /* Make sure socket is routed, for correct metrics. */
                icsk->icsk_af_ops->rebuild_header(sk);
                tcp_init_congestion_control(sk);
    
                tcp_mtup_init(sk);
                tp->copied_seq = tp->rcv_nxt;
                tcp_init_buffer_space(sk);
            }
            smp_mb();
            tcp_set_state(sk, TCP_ESTABLISHED);
            sk->sk_state_change(sk);
    
            /* Note, that this wakeup is only for marginal crossed SYN case.
             * Passively open sockets are not waked up, because
             * sk->sk_sleep == NULL and sk->sk_socket == NULL.
             */
            if (sk->sk_socket)
                sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT);
    
            tp->snd_una = TCP_SKB_CB(skb)->ack_seq;
            tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale;
            tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);
            tcp_synack_rtt_meas(sk, synack_stamp);
    
            if (tp->rx_opt.tstamp_ok)
                tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
    
            if (req) {
                /* Re-arm the timer because data may have been sent out.
                 * This is similar to the regular data transmission case
                 * when new data has just been ack'ed.
                 *
                 * (TFO) - we could try to be more aggressive and
                 * retransmitting any data sooner based on when they
                 * are sent out.
                 */
                tcp_rearm_rto(sk);
            } else
                tcp_init_metrics(sk);
    
            tcp_update_pacing_rate(sk);
    
            /* Prevent spurious tcp_cwnd_restart() on first data packet */
            tp->lsndtime = tcp_time_stamp;
    
            tcp_initialize_rcv_mss(sk);
            tcp_fast_path_on(tp);
            break;
    
        case TCP_FIN_WAIT1: {
            struct dst_entry *dst;
            int tmo;
    
            /* If we enter the TCP_FIN_WAIT1 state and we are a
             * Fast Open socket and this is the first acceptable
             * ACK we have received, this would have acknowledged
             * our SYNACK so stop the SYNACK timer.
             */
            if (req != NULL) {
                /* Return RST if ack_seq is invalid.
                 * Note that RFC793 only says to generate a
                 * DUPACK for it but for TCP Fast Open it seems
                 * better to treat this case like TCP_SYN_RECV
                 * above.
                 */
                if (!acceptable)
                    return 1;
                /* We no longer need the request sock. */
                reqsk_fastopen_remove(sk, req, false);
                tcp_rearm_rto(sk);
            }
            if (tp->snd_una != tp->write_seq)
                break;
    
            tcp_set_state(sk, TCP_FIN_WAIT2);
            sk->sk_shutdown |= SEND_SHUTDOWN;
    
            dst = __sk_dst_get(sk);
            if (dst)
                dst_confirm(dst);
    
            if (!sock_flag(sk, SOCK_DEAD)) {
                /* Wake up lingering close() */
                sk->sk_state_change(sk);
                break;
            }
    
            if (tp->linger2 < 0 ||
                (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
                 after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) {
                tcp_done(sk);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
                return 1;
            }
    
            tmo = tcp_fin_time(sk);
            if (tmo > TCP_TIMEWAIT_LEN) {
                inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN);
            } else if (th->fin || sock_owned_by_user(sk)) {
                /* Bad case. We could lose such FIN otherwise.
                 * It is not a big problem, but it looks confusing
                 * and not so rare event. We still can lose it now,
                 * if it spins in bh_lock_sock(), but it is really
                 * marginal case.
                 */
                inet_csk_reset_keepalive_timer(sk, tmo);
            } else {
                tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
                goto discard;
            }
            break;
        }
    
        case TCP_CLOSING:
            if (tp->snd_una == tp->write_seq) {
                tcp_time_wait(sk, TCP_TIME_WAIT, 0);
                goto discard;
            }
            break;
    
        case TCP_LAST_ACK:
            if (tp->snd_una == tp->write_seq) {
                tcp_update_metrics(sk);
                tcp_done(sk);
                goto discard;
            }
            break;
        }
    
        /* step 6: check the URG bit */
        tcp_urg(sk, skb, th);
    
        /* step 7: process the segment text */
        switch (sk->sk_state) {
        case TCP_CLOSE_WAIT:
        case TCP_CLOSING:
        case TCP_LAST_ACK:
            if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
                break;
        case TCP_FIN_WAIT1:
        case TCP_FIN_WAIT2:
            /* RFC 793 says to queue data in these states,
             * RFC 1122 says we MUST send a reset.
             * BSD 4.4 also does reset.
             */
            if (sk->sk_shutdown & RCV_SHUTDOWN) {
                if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
                    after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
                    NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
                    tcp_reset(sk);
                    return 1;
                }
            }
            /* Fall through */
        case TCP_ESTABLISHED:
            tcp_data_queue(sk, skb);
            queued = 1;
            break;
        }
    
        /* tcp_data could move socket to TIME-WAIT */
        if (sk->sk_state != TCP_CLOSE) {
            tcp_data_snd_check(sk);
            tcp_ack_snd_check(sk);
        }
    
        if (!queued) {
    discard:
            __kfree_skb(skb);
        }
        return 0;
    }
    EXPORT_SYMBOL(tcp_rcv_state_process);

    此函数会对连接状态进行判断,找到 客户端的sock 状态为 TCP_SYN_SENT

    如下

    case TCP_SYN_SENT:
            queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
            if (queued >= 0)
                return queued;
    
            /* Do step6 onward by hand. */
            tcp_urg(sk, skb, th);
            __kfree_skb(skb);
            tcp_data_snd_check(sk);
            return 0;
        }

    会调用函数

    tcp_rcv_synsent_state_process我们看一下这个函数
    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->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;
    }
    摘取这部分代码:关于对 TCB 状态为TCP_SYN_SENT  状态的sock
    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);

    tcp_sned_sk 将该sock 发送给客户端,此时该 sock 的状态为 TCP_SYN_RECV状态。

  • 相关阅读:
    http url转义字符,特殊字符
    No bean named &#39;cxf&#39; is defined
    c语言中结构体指针
    Android fragment (二)
    文件I/O之C标准库函数和系统库函数差别
    计算机组成原理——主存与cache的映射关系
    openstack 用nova API 指定 compute node 创建 instance
    SQL存在一个表而不在还有一个表中的数据
    hdu 2602
    小金登陆游戏
  • 原文地址:https://www.cnblogs.com/guoyu1024/p/10591147.html
Copyright © 2011-2022 走看看