TCP复位报文的发送

内核发送reset报文的函数为tcp_v4_send_reset,如下其赋值给了tcp_request_sock_ops结构的成员send_reset。

struct request_sock_ops tcp_request_sock_ops __read_mostly = {
    .family     =   PF_INET,
    ...
    .send_reset =   tcp_v4_send_reset,

监听套接口不存在

如下tcp_v4_rcv函数中,__inet_lookup_skb没有找到监听套接口,跳转到no_tcp_socket标签处,如果此触发报文的checksum没有问题,将回复Reset报文。另外在函数tcp_v4_send_reset中会检查当前报文是否设置了reset标志位,不对接收到的reset报文回复reset报文。

int tcp_v4_rcv(struct sk_buff *skb)
{

    th = (const struct tcphdr *)skb->data;
    iph = ip_hdr(skb);
lookup:
    sk = __inet_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), th->source,
                   th->dest, sdif, &refcounted);
    if (!sk)
        goto no_tcp_socket;

process:
    if (sk->sk_state == TCP_TIME_WAIT)
        goto do_time_wait;

这里为找不到套接口,发送reset报文的位置。

no_tcp_socket:
    ...
    if (tcp_checksum_complete(skb)) {
csum_error:
        __TCP_INC_STATS(net, TCP_MIB_CSUMERRORS);
bad_packet:
        __TCP_INC_STATS(net, TCP_MIB_INERRS);
    } else {
        tcp_v4_send_reset(NULL, skb);
    }

握手阶段不合法ACK

接着上一节,如果找到了监听套接口,并且其状态为TCP_NEW_SYN_RECV,而且成功创建了子套接口,但是在子套接口处理函数tcp_child_process中返回错误,也将发送reset报文。错误情况稍后在函数tcp_rcv_state_process中介绍。

int tcp_v4_rcv(struct sk_buff *skb)
{

    if (sk->sk_state == TCP_NEW_SYN_RECV) {
        struct request_sock *req = inet_reqsk(sk);
        ...
        if (!tcp_filter(sk, skb)) {
            th = (const struct tcphdr *)skb->data;
            iph = ip_hdr(skb);
            tcp_v4_fill_cb(skb, iph, th);
            nsk = tcp_check_req(sk, skb, req, false, &req_stolen);
        }
        ...
        if (nsk == sk) {
            reqsk_put(req);
            tcp_v4_restore_cb(skb);
        } else if (tcp_child_process(sk, nsk, skb)) {
            tcp_v4_send_reset(nsk, skb);
            goto discard_and_relse;

如下tcp_rcv_state_process函数,在处理ACK报文的函数tcp_ack返回值小于0时,认为是一个不可接受的非法ACK报文,返回1,由函数tcp_v4_rcv发送Reset报文。注意在此之前已经由函数tcp_validate_incoming进行了PAWS和序号等的合法性检查,对于非法报文不回复RESET报文。

这里仅限于对三次握手中的ACK处理。

int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
{
    ...
    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 |
                      FLAG_NO_CHALLENGE_ACK) > 0;

    if (!acceptable) {
        if (sk->sk_state == TCP_SYN_RECV)
            return 1;   /* send one RST */

以下看一看函数tcp_ack对ACK报文的检查,如果接收到的ACK报文确认的是已经确认过的数据,判定为old_ack;再进一步其确认序号与当前待确认序号相差大于max_window,发送challenge-ACK,以便对端调整序号。

另外如果ACK确认的数据,本地还没有发送,判定为无效的ACK(invalid_ack)。这里的三种情况,都将导致tcp_ack函数返回小于或者等于0的值,参考以上的tcp_rcv_state_process函数,如果此时套接口的状态为TCP_SYN_RECV,回复reset报文,断开连接。

static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
{
    u32 prior_snd_una = tp->snd_una;
    u32 ack_seq = TCP_SKB_CB(skb)->seq;
    u32 ack = TCP_SKB_CB(skb)->ack_seq;

    /* If the ack is older than previous acks then we can probably ignore it.
     */
    if (before(ack, prior_snd_una)) {
        /* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */
        if (before(ack, prior_snd_una - tp->max_window)) {
            if (!(flag & FLAG_NO_CHALLENGE_ACK))
                tcp_send_challenge_ack(sk, skb);
            return -1;
        }
        goto old_ack;
    }
    /* If the ack includes data we haven't sent yet, discard
     * this segment (RFC793 Section 3.9).
     */
    if (after(ack, tp->snd_nxt))
        goto invalid_ack;

错误的SYN报文

如下函数tcp_v4_rcv中,对于套接口处于TCP_NEW_SYN_RECV状态接收的报文,由函数tcp_check_req检查。

int tcp_v4_rcv(struct sk_buff *skb)
{

    if (sk->sk_state == TCP_NEW_SYN_RECV) {
        struct request_sock *req = inet_reqsk(sk);
        sk = req->rsk_listener;
		 
        if (!tcp_filter(sk, skb)) {
            th = (const struct tcphdr *)skb->data;
            iph = ip_hdr(skb);
            tcp_v4_fill_cb(skb, iph, th);
            nsk = tcp_check_req(sk, skb, req, false, &req_stolen);
        }

此时,应当接收到客户端三次握手的ACK报文,但是实际接收的报文设置了TCP_FLAG_RST或者TCP_FLAG_SYN标志,并且此报文也不属于重传的SYN报文。

struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
               struct request_sock *req, bool fastopen, bool *req_stolen)
{
    __be32 flg = tcp_flag_word(th) & (TCP_FLAG_RST|TCP_FLAG_SYN|TCP_FLAG_ACK);

    /* Check for pure retransmitted SYN. */
    if (TCP_SKB_CB(skb)->seq == tcp_rsk(req)->rcv_isn &&
        flg == TCP_FLAG_SYN &&
        !paws_reject) {
        ...
        return NULL;
    }   

    /* RFC793: "first check sequence number". */
    if (paws_reject || !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
                      tcp_rsk(req)->rcv_nxt, tcp_rsk(req)->rcv_nxt + req->rsk_rcv_wnd)) {
        /* Out of window: send ACK and drop. */
        ...
    }

    /* RFC793: "second check the RST bit" and "fourth, check the SYN bit"
     */
    if (flg & (TCP_FLAG_RST|TCP_FLAG_SYN)) {
        __TCP_INC_STATS(sock_net(sk), TCP_MIB_ATTEMPTFAILS);
        goto embryonic_reset;
    }

如果当前报文没有设置RST标志位,发送RESET报文。否则,如果设置了RST标志位,释放请求套接口结构(inet_csk_reqsk_queue_drop);

embryonic_reset:
    if (!(flg & TCP_FLAG_RST)) {
        /* Received a bad SYN pkt - for TFO We try not to reset
         * the local connection unless it's really necessary to
         * avoid becoming vulnerable to outside attack aiming at
         * resetting legit local connections.
         */
        req->rsk_ops->send_reset(sk, skb);
    } else if (fastopen) { /* received a valid RST pkt */
        reqsk_fastopen_remove(sk, req, true);
        tcp_reset(sk);
    }
    if (!fastopen) {
        inet_csk_reqsk_queue_drop(sk, req);
        __NET_INC_STATS(sock_net(sk), LINUX_MIB_EMBRYONICRSTS);
    } 

挥手阶段的RESET报文

对于TCP_TIME_WAIT状态的套接口,当函数tcp_timewait_state_process返回值为TCP_TW_RST时,发送reset报文,有以下两者情况。

int tcp_v4_rcv(struct sk_buff *skb)
{

do_time_wait:
    ...
    switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
    ...
    case TCP_TW_RST:
        tcp_v4_send_reset(sk, skb);
        inet_twsk_deschedule_put(inet_twsk(sk));
        goto discard_it;
    case TCP_TW_SUCCESS:;
    }
    goto discard_it;
}

在函数tcp_timewait_state_process中,如果套接口状态为TCP_FIN_WAIT2,即本端已经关闭连接,正在等待对端的FIN报文,此时接收到设置SYN标志的报文,并且其序号位于待接收序号tw_rcv_nxt之后,即对端要错误的发起新的三次握手,回复RESET报文。

enum tcp_tw_status
tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, const struct tcphdr *th)
{
    struct tcp_options_received tmp_opt;
    struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
    ...

    if (tw->tw_substate == TCP_FIN_WAIT2) {
        /* Just repeat all the checks of tcp_rcv_state_process() */
        /* Out of window, send ACK */
        if (paws_reject ||
            !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
                   tcptw->tw_rcv_nxt,
                   tcptw->tw_rcv_nxt + tcptw->tw_rcv_wnd))
            return tcp_timewait_check_oow_rate_limit(
                tw, skb, LINUX_MIB_TCPACKSKIPPEDFINWAIT2);

        if (th->rst) goto kill;
        if (th->syn && !before(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt))
            return TCP_TW_RST;

如果报文没有设置ACK标志,或者ACK报文的序号在待接收序号tw_rcv_nxt之前,即接收到重复的ACK报文,返回TCP_TW_SUCCESS,释放此报文。再者,如果当前报文没有设置FIN标志,或者设置了FIN,但是报文的结束序号end_seq不等于待接收序号加一(FIN占用一个序号),表明FIN报文中携带有数据,回复RESET报文。

        /* Dup ACK? */
        if (!th->ack ||
            !after(TCP_SKB_CB(skb)->end_seq, tcptw->tw_rcv_nxt) ||
            TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq) {
            inet_twsk_put(tw);
            return TCP_TW_SUCCESS;
        }

        /* New data or FIN. If new data arrive after half-duplex close, reset.
         */
        if (!th->fin ||
            TCP_SKB_CB(skb)->end_seq != tcptw->tw_rcv_nxt + 1)
            return TCP_TW_RST;

TCP状态机处理

以上主要是在TCP的连接建立和断开阶段,触发的RESET报文。在以下函数tcp_v4_do_rcv中,TCP状态机处理函数tcp_rcv_state_process返回不等于0的值,也将导致reset报文的发送。

int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
    ...

    if (tcp_checksum_complete(skb))
        goto csum_err;

    if (sk->sk_state == TCP_LISTEN) {
        struct sock *nsk = tcp_v4_cookie_check(sk, skb);
        if (!nsk) goto discard;
        if (nsk != sk) {
            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)) {
        rsk = sk;
        goto reset;
    }
    return 0;

reset:
    tcp_v4_send_reset(rsk, skb);

如下TCP_LISTEN状态的监听套接口接收到带有ACK标志的报文,回复RESET报文。如果接收到SYN报文,交由IPv4/IPv6协议的conn_request指针函数处理,当其返回值小于零时,表明SYN报文不可接受返回1,由以上函数tcp_v4_do_rcv发送reset报文,但是目前conn_request不会返回小于零的值。

int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct inet_connection_sock *icsk = inet_csk(sk);
    const struct tcphdr *th = tcp_hdr(skb);

    switch (sk->sk_state) {
    case TCP_LISTEN:
        if (th->ack)
            return 1;

        if (th->rst)
            goto discard;

        if (th->syn) {
            if (th->fin)
                goto discard;
            /* It is possible that we process SYN packets from backlog,
             * so we need to make sure to disable BH and RCU right there.
             */
            rcu_read_lock();
            local_bh_disable();
            acceptable = icsk->icsk_af_ops->conn_request(sk, skb) >= 0;
            local_bh_enable();
            rcu_read_unlock();

            if (!acceptable)
                return 1;

如果套接口状态为TCP_SYN_SENT(TCP客户端),当前接收到的回复报文设置了ACK标志,但是确认序号不合法,回复RESET报文,参见函数tcp_rcv_synsent_state_process中的处理。

    case TCP_SYN_SENT:
        tp->rx_opt.saw_tstamp = 0;
        tcp_mstamp_refresh(tp);
        queued = tcp_rcv_synsent_state_process(sk, skb, th);
        if (queued >= 0)
            return queued;

对于进入TCP_FIN_WAIT1状态的套接口,接收到ACK报文,状态转变为TCP_FIN_WAIT2,如果选项TCP_LINGER2小于零,立即关闭并释放此套接口,未处理的数据将被释放,返回1,作为错误指示回复对端reset报文。

另外,如果此时接收到报文起始序号seq与结束序号end_seq不相同,不是纯ACK报文,并且结束序号位于待接收序号之后,表明为乱序的数据包,或者乱序的FIN报文,同样回复reset报文。

    switch (sk->sk_state) {
    case TCP_FIN_WAIT1: {
        ...
        tcp_set_state(sk, TCP_FIN_WAIT2);
        sk->sk_shutdown |= SEND_SHUTDOWN;
        ...

        if (tp->linger2 < 0) {
            tcp_done(sk);
            NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
            return 1;
        }
        if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq &&
            after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) {
            /* Receive out of order FIN after close() */
            if (tp->syn_fastopen && th->fin)
                tcp_fastopen_active_disable(sk);
            tcp_done(sk);
            NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
            return 1;
        }

套接口处于以下几种状态时,本地已经关闭接收(RCV_SHUTDOWN),但是又接收到数据时(此报文设置有RST标志),回复对端reset报文。

    /* 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;
        /* fall through */
    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(sock_net(sk), LINUX_MIB_TCPABORTONDATA);
                tcp_reset(sk);
                return 1;

主动发送reset发送

以上介绍的情况都是使用tcp_v4_send_reset发送复位报文,内核中还有另一个函数tcp_send_active_reset可发送复位报文。注释中说明在显式调用close关闭套接口,或者exit调用触发关闭套接口是,如果套接口接收队列中还有未读的数据时,使用此函数向对端发送reset报文。

/* We get here when a process closes a file descriptor (either due to
 * an explicit close() or as a byproduct of exit()'ing) and there
 * was unread data in the receive queue.  This behavior is recommended
 * by RFC 2525, section 2.17.  -DaveM
 */         
void tcp_send_active_reset(struct sock *sk, gfp_t priority)
{           
    struct sk_buff *skb;
        
    TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);

    /* NOTE: No TCP options attached and we never retransmit this. */
    skb = alloc_skb(MAX_TCP_HEADER, priority);
    if (!skb) {         
        NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED);
        return; 
    }           
            
    /* Reserve space for headers and prepare control bits. */
    skb_reserve(skb, MAX_TCP_HEADER);
    tcp_init_nondata_skb(skb, tcp_acceptable_seq(sk),
                 TCPHDR_ACK | TCPHDR_RST);
    tcp_mstamp_refresh(tcp_sk(sk));
    /* Send it off. */
    if (tcp_transmit_skb(sk, skb, 0, priority))
        NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED);

函数tcp_check_oom检查当前系统中的orphan套接口数量是否超过/proc/sys/net/ipv4/tcp_max_orphans中规定的值; 或者,系统中套接口使用的内存空间超出/proc/sys/net/ipv4/tcp_mem中定义的最大内存空间,认定为发送OOM。如果此时,1)上一个报文刚刚发送;或者2)发送窗口为0,没有in-flight报文,向对端发送复位报文。

static int tcp_out_of_resources(struct sock *sk, bool do_reset)
{
    struct tcp_sock *tp = tcp_sk(sk);
    int shift = 0;

    /* If peer does not open window for long time, or did not transmit
     * anything for long time, penalize it. */
    if ((s32)(tcp_jiffies32 - tp->lsndtime) > 2*TCP_RTO_MAX || !do_reset)
        shift++;

    /* If some dubious ICMP arrived, penalize even more. */
    if (sk->sk_err_soft)
        shift++;

    if (tcp_check_oom(sk, shift)) {
        /* Catch exceptional cases, when connection requires reset.
         *      1. Last segment was sent recently. */
        if ((s32)(tcp_jiffies32 - tp->lsndtime) <= TCP_TIMEWAIT_LEN ||
            /*  2. Window is closed. */
            (!tp->snd_wnd && !tp->packets_out))
            do_reset = true;
        if (do_reset)
            tcp_send_active_reset(sk, GFP_ATOMIC);
        tcp_done(sk);
        __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY);

在如下的tcp_keepalive_timer保活处理函数中,如果套接口状态为TCP_FIN_WAIT2,并且其已经设置了SOCK_DEAD标志(例如变为orphan套接口),此时如果linger2时长小于0时,向对端发送reset报文。或者,linger2时长大于等于0,但是计算而得的剩余超时时长小于零,同样发送reset报文。

static void tcp_keepalive_timer (struct timer_list *t)
{
    struct sock *sk = from_timer(sk, t, sk_timer);
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct tcp_sock *tp = tcp_sk(sk);

    ...
    tcp_mstamp_refresh(tp);
    if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) {
        if (tp->linger2 >= 0) {
            const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN;

            if (tmo > 0) {
                tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
                goto out;
            }
        }
        tcp_send_active_reset(sk, GFP_ATOMIC);
        goto death;
    }

如果已经发送过探测报文,并且上次接收到对端报文到当下经过的时长超过了用户指定的时长icsk_user_timeout; 或者,用户未指定超时时长,但是发送探测报文的次数已经超限,向对端发送reset报文。

    elapsed = keepalive_time_elapsed(tp);
    
    if (elapsed >= keepalive_time_when(tp)) {
        /* If the TCP_USER_TIMEOUT option is enabled, use that
         * to determine when to timeout instead.
         */
        if ((icsk->icsk_user_timeout != 0 &&
            elapsed >= msecs_to_jiffies(icsk->icsk_user_timeout) &&
            icsk->icsk_probes_out > 0) ||
            (icsk->icsk_user_timeout == 0 &&
            icsk->icsk_probes_out >= keepalive_probes(tp))) {
            tcp_send_active_reset(sk, GFP_ATOMIC);
            tcp_write_err(sk);

套接口关闭错误

在关闭套接口时,接收队列中还有未读取的数据(data_was_unread),向对端发送reset报文。

void tcp_close(struct sock *sk, long timeout)
{
    ...
    while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
        u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq;

        if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
            len--;
        data_was_unread += len;
        __kfree_skb(skb);
    }

    /* If socket has been already reset (e.g. in tcp_reset()) - kill it. */
    if (sk->sk_state == TCP_CLOSE)
        goto adjudge_to_death;

    if (unlikely(tcp_sk(sk)->repair)) {
        sk->sk_prot->disconnect(sk, 0);
    } else if (data_was_unread) {
        /* Unread data was tossed, zap the connection. */
        NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
        tcp_set_state(sk, TCP_CLOSE);
        tcp_send_active_reset(sk, sk->sk_allocation);
    } else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
        /* Check zero linger _after_ checking for unread data. */
        sk->sk_prot->disconnect(sk, 0);
        NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA);

当套接口处于TCP_FIN_WAIT2状态,linger2选项小于零时,向对端发送reset报文。另外,如果套接口没有置成TCP_CLOSE状态,作为一个检查OOM的时机,调用tcp_check_oom函数,如果发生OOM,向对端发送reset报文。

    if (sk->sk_state == TCP_FIN_WAIT2) {
        struct tcp_sock *tp = tcp_sk(sk);
        if (tp->linger2 < 0) {
            tcp_set_state(sk, TCP_CLOSE);
            tcp_send_active_reset(sk, GFP_ATOMIC);
            __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONLINGER);
        } else {
            ...
        }
    }
    if (sk->sk_state != TCP_CLOSE) {
        sk_mem_reclaim(sk);
        if (tcp_check_oom(sk, 0)) {
            tcp_set_state(sk, TCP_CLOSE);
            tcp_send_active_reset(sk, GFP_ATOMIC);
            __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY);

以上tcp_close函数中调用的sk->sk_prot->disconnect指针指向函数tcp_disconnect,如果套接口开启了SOCK_LINGER选项(与TCP层的linger2不同),并且sk_lingertime时长不为零,调用以下的tcp_disconnect函数。

如果套接口状态不等于TCP_LISTEN,并且符合tcp_need_reset函数中定义的状态; 或者还有数据没有发送,并且套接口状态为TCPF_CLOSING或者TCPF_LAST_ACK,向对端发送reset报文。

int tcp_disconnect(struct sock *sk, int flags)
{
    struct inet_sock *inet = inet_sk(sk);
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct tcp_sock *tp = tcp_sk(sk);
    int old_state = sk->sk_state;

    if (old_state != TCP_CLOSE)
        tcp_set_state(sk, TCP_CLOSE);

    /* ABORT function of RFC793 */
    if (old_state == TCP_LISTEN) {
        inet_csk_listen_stop(sk);
    } else if (unlikely(tp->repair)) {
        sk->sk_err = ECONNABORTED;
    } else if (tcp_need_reset(old_state) ||
           (tp->snd_nxt != tp->write_seq &&
            (1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK))) {
        /* The last check adjusts for discrepancy of Linux wrt. RFC states
         */
        tcp_send_active_reset(sk, gfp_any());
        sk->sk_err = ECONNRESET;

static inline bool tcp_need_reset(int state)
{
    return (1 << state) &
           (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT | TCPF_FIN_WAIT1 |
        TCPF_FIN_WAIT2 | TCPF_SYN_RECV);

函数tcp_abort在销毁诊断套接口时被调用,如果此时套接口还没有正常关闭(SOCK_DEAD),根据tcp_need_reset的返回值,就决定是否发送reset报文。

int tcp_abort(struct sock *sk, int err)
{
    ...
    if (!sock_flag(sk, SOCK_DEAD)) {
        sk->sk_err = err;
        /* This barrier is coupled with smp_rmb() in tcp_poll() */
        smp_wmb();
        sk->sk_error_report(sk);
        if (tcp_need_reset(sk->sk_state))
            tcp_send_active_reset(sk, GFP_ATOMIC);
        tcp_done(sk);

内核版本 5.0

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页