searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

端口复用遇到 NAT 导致的TCP连接超时

2023-05-18 02:46:29
319
0

现象

云主机通过 NAT 网元与对象存储网关进行交互,在云主机上持续观测到零星HTTP请求超时。

 

问题定位

客户端抓包,可见如下报文,首先怀疑服务端丢弃了RST后的SYN报文,导致连接无法建立。

联系相关同事在NAT网元上抓包,转发网抓包结果如下,可见转发网收到了服务端响应的SYN+ACK报文,但云主机侧未抓到该报文,问题可能出在NAT网关未发出或宿主机未投递该报文,需要进一步抓包分析确认。

 

 

疑问

服务端为何对 SYN 报文响应了普通的 ACK, 且 Seq不匹配?

对此继续分析转发网报文,可见在该端口用于故障连接前,曾被用于其他的连接,见下图。

 

由报文可见,4365端口在连接关闭后的不到 20s 被用于新的连接。根据报文交互,四次挥手已完成,130端处于TIME_WAIT 状态, 该端口默认情况下需要等待 2MSL 才能用于新的连接。由于服务端配置了 net.ipv4.tcp_tw_reuse。 内核 TCP 实现可见,根据 RFC 1122,服务端仅可在新来的 SYN 序号大于 TIME_WAIT socket 记录的已用序号时, 才能接受新的 SYN报文, 建立新连接, 否则将视情况返回 旧连接中的 ACK 报文。 此处新的SYN seq 为4119633515 ,旧连接已使用的 seq 为 693821462, 4119633515 在 693821462 之后,为何没有回复 SYN+ACK呢?

Linux内核实现代码如下,可见是 通过 (after(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt) 来判断序号的大小关系的。

enum tcp_tw_status
tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
			   const struct tcphdr *th)
{
    .......
    ....


        if (th->syn && !th->rst && !th->ack && !paws_reject &&
            (after(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt) ||
             (tmp_opt.saw_tstamp &&
              (s32)(tcptw->tw_ts_recent - tmp_opt.rcv_tsval) < 0))) {
            u32 isn = tcptw->tw_snd_nxt + 65535 + 2;
            if (isn == 0)
                isn++;
            TCP_SKB_CB(skb)->tcp_tw_isn = isn;
            return TCP_TW_SYN;
        }
       	if (paws_reject)
    		__NET_INC_STATS(twsk_net(tw), LINUX_MIB_PAWSESTABREJECTED);

    	if (!th->rst) {
    		/* In this case we must reset the TIMEWAIT timer.
    		 *
    		 * If it is ACKless SYN it may be both old duplicate
    		 * and new good SYN with random sequence number <rcv_nxt.
    		 * Do not reschedule in the last case.
    		 */
    		if (paws_reject || th->ack)
    			inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN);

    		return tcp_timewait_check_oow_rate_limit(
    			tw, skb, LINUX_MIB_TCPACKSKIPPEDTIMEWAIT);
    	}

....
....
    
}

 

其中after宏的定义如下,  after(4119633515 , 693821462) ==> before(693821462,4119633515 )  ==> (__s32) (693821462 - 4119633515) = 869155243 **>0**,  因此 不满足after条件,因而 130 端回复了 旧连接的 ACK 报文, 由此分析 130 端回复重复的ACK是 Linux TCP 协议栈实现的正常行为。

/*
 * The next routines deal with comparing 32 bit unsigned ints
 * and worry about wraparound (automatic with unsigned arithmetic).
 */

static inline bool before(__u32 seq1, __u32 seq2)
{
        return (__s32)(seq1-seq2) < 0;
}
#define after(seq2, seq1) 	before(seq1, seq2)

 

 

## 结论

此问题由 NAT 网关 端口复用引起, 2MSL内复用端口,对TCP的特殊情况处理欠缺,导致报文投递存在问题,导致连接无法建立;需要在宿主机和NAT网关进一步分析报文未投递到云主机的原因。

参考代码: tcp_timewait_state_process

 

/*
 * * Main purpose of TIME-WAIT state is to close connection gracefully,
 *   when one of ends sits in LAST-ACK or CLOSING retransmitting FIN
 *   (and, probably, tail of data) and one or more our ACKs are lost.
 * * What is TIME-WAIT timeout? It is associated with maximal packet
 *   lifetime in the internet, which results in wrong conclusion, that
 *   it is set to catch "old duplicate segments" wandering out of their path.
 *   It is not quite correct. This timeout is calculated so that it exceeds
 *   maximal retransmission timeout enough to allow to lose one (or more)
 *   segments sent by peer and our ACKs. This time may be calculated from RTO.
 * * When TIME-WAIT socket receives RST, it means that another end
 *   finally closed and we are allowed to kill TIME-WAIT too.
 * * Second purpose of TIME-WAIT is catching old duplicate segments.
 *   Well, certainly it is pure paranoia, but if we load TIME-WAIT
 *   with this semantics, we MUST NOT kill TIME-WAIT state with RSTs.
 * * If we invented some more clever way to catch duplicates
 *   (f.e. based on PAWS), we could truncate TIME-WAIT to several RTOs.
 *
 * The algorithm below is based on FORMAL INTERPRETATION of RFCs.
 * When you compare it to RFCs, please, read section SEGMENT ARRIVES
 * from the very beginning.
 *
 * NOTE. With recycling (and later with fin-wait-2) TW bucket
 * is _not_ stateless. It means, that strictly speaking we must
 * spinlock it. I do not want! Well, probability of misbehaviour
 * is ridiculously low and, seems, we could use some mb() tricks
 * to avoid misread sequence numbers, states etc.  --ANK
 *
 * We don't need to initialize tmp_out.sack_ok as we don't use the results
 */
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);
	bool paws_reject = false;

	tmp_opt.saw_tstamp = 0;
	if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) {
		tcp_parse_options(twsk_net(tw), skb, &tmp_opt, 0, NULL);

		if (tmp_opt.saw_tstamp) {
			if (tmp_opt.rcv_tsecr)
				tmp_opt.rcv_tsecr -= tcptw->tw_ts_offset;
			tmp_opt.ts_recent	= tcptw->tw_ts_recent;
			tmp_opt.ts_recent_stamp	= tcptw->tw_ts_recent_stamp;
			paws_reject = tcp_paws_reject(&tmp_opt, th->rst);
		}
	}

	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;

		/* 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;

		/* FIN arrived, enter true time-wait state. */
		tw->tw_substate	  = TCP_TIME_WAIT;
		tcptw->tw_rcv_nxt = TCP_SKB_CB(skb)->end_seq;
		if (tmp_opt.saw_tstamp) {
			tcptw->tw_ts_recent_stamp = ktime_get_seconds();
			tcptw->tw_ts_recent	  = tmp_opt.rcv_tsval;
		}

		inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN);
		return TCP_TW_ACK;
	}

	/*
	 *	Now real TIME-WAIT state.
	 *
	 *	RFC 1122:
	 *	"When a connection is [...] on TIME-WAIT state [...]
	 *	[a TCP] MAY accept a new SYN from the remote TCP to
	 *	reopen the connection directly, if it:
	 *
	 *	(1)  assigns its initial sequence number for the new
	 *	connection to be larger than the largest sequence
	 *	number it used on the previous connection incarnation,
	 *	and
	 *
	 *	(2)  returns to TIME-WAIT state if the SYN turns out
	 *	to be an old duplicate".
	 */

	if (!paws_reject &&
	    (TCP_SKB_CB(skb)->seq == tcptw->tw_rcv_nxt &&
	     (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq || th->rst))) {
		/* In window segment, it may be only reset or bare ack. */

		if (th->rst) {
			/* This is TIME_WAIT assassination, in two flavors.
			 * Oh well... nobody has a sufficient solution to this
			 * protocol bug yet.
			 */
			if (twsk_net(tw)->ipv4.sysctl_tcp_rfc1337 == 0) {
kill:
				inet_twsk_deschedule_put(tw);
				return TCP_TW_SUCCESS;
			}
		} else {
			inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN);
		}

		if (tmp_opt.saw_tstamp) {
			tcptw->tw_ts_recent	  = tmp_opt.rcv_tsval;
			tcptw->tw_ts_recent_stamp = ktime_get_seconds();
		}

		inet_twsk_put(tw);
		return TCP_TW_SUCCESS;
	}

	/* Out of window segment.

	   All the segments are ACKed immediately.

	   The only exception is new SYN. We accept it, if it is
	   not old duplicate and we are not in danger to be killed
	   by delayed old duplicates. RFC check is that it has
	   newer sequence number works at rates <40Mbit/sec.
	   However, if paws works, it is reliable AND even more,
	   we even may relax silly seq space cutoff.

	   RED-PEN: we violate main RFC requirement, if this SYN will appear
	   old duplicate (i.e. we receive RST in reply to SYN-ACK),
	   we must return socket to time-wait state. It is not good,
	   but not fatal yet.
	 */

	if (th->syn && !th->rst && !th->ack && !paws_reject &&
	    (after(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt) ||
	     (tmp_opt.saw_tstamp &&
	      (s32)(tcptw->tw_ts_recent - tmp_opt.rcv_tsval) < 0))) {
		u32 isn = tcptw->tw_snd_nxt + 65535 + 2;
		if (isn == 0)
			isn++;
		TCP_SKB_CB(skb)->tcp_tw_isn = isn;
		return TCP_TW_SYN;
	}

	if (paws_reject)
		__NET_INC_STATS(twsk_net(tw), LINUX_MIB_PAWSESTABREJECTED);

	if (!th->rst) {
		/* In this case we must reset the TIMEWAIT timer.
		 *
		 * If it is ACKless SYN it may be both old duplicate
		 * and new good SYN with random sequence number <rcv_nxt.
		 * Do not reschedule in the last case.
		 */
		if (paws_reject || th->ack)
			inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN);

		return tcp_timewait_check_oow_rate_limit(
			tw, skb, LINUX_MIB_TCPACKSKIPPEDTIMEWAIT);
	}
	inet_twsk_put(tw);
	return TCP_TW_SUCCESS;
}
EXPORT_SYMBOL(tcp_timewait_state_process);

 

服务端返回了 旧的ACK, 导致 Client 发送了 RST 报文。 服务端收到 RST 后, 旧的socket将被kill,再到来的 SYN 可以建立新连接,此时重传的 SYN 在服务端应该正常响应 SYN+ACK, 因此服务端继续回复 SYN + ACK 的行为是符合预期的正常行为。

0条评论
0 / 1000
刘****润
2文章数
1粉丝数
刘****润
2 文章 | 1 粉丝
刘****润
2文章数
1粉丝数
刘****润
2 文章 | 1 粉丝
原创

端口复用遇到 NAT 导致的TCP连接超时

2023-05-18 02:46:29
319
0

现象

云主机通过 NAT 网元与对象存储网关进行交互,在云主机上持续观测到零星HTTP请求超时。

 

问题定位

客户端抓包,可见如下报文,首先怀疑服务端丢弃了RST后的SYN报文,导致连接无法建立。

联系相关同事在NAT网元上抓包,转发网抓包结果如下,可见转发网收到了服务端响应的SYN+ACK报文,但云主机侧未抓到该报文,问题可能出在NAT网关未发出或宿主机未投递该报文,需要进一步抓包分析确认。

 

 

疑问

服务端为何对 SYN 报文响应了普通的 ACK, 且 Seq不匹配?

对此继续分析转发网报文,可见在该端口用于故障连接前,曾被用于其他的连接,见下图。

 

由报文可见,4365端口在连接关闭后的不到 20s 被用于新的连接。根据报文交互,四次挥手已完成,130端处于TIME_WAIT 状态, 该端口默认情况下需要等待 2MSL 才能用于新的连接。由于服务端配置了 net.ipv4.tcp_tw_reuse。 内核 TCP 实现可见,根据 RFC 1122,服务端仅可在新来的 SYN 序号大于 TIME_WAIT socket 记录的已用序号时, 才能接受新的 SYN报文, 建立新连接, 否则将视情况返回 旧连接中的 ACK 报文。 此处新的SYN seq 为4119633515 ,旧连接已使用的 seq 为 693821462, 4119633515 在 693821462 之后,为何没有回复 SYN+ACK呢?

Linux内核实现代码如下,可见是 通过 (after(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt) 来判断序号的大小关系的。

enum tcp_tw_status
tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
			   const struct tcphdr *th)
{
    .......
    ....


        if (th->syn && !th->rst && !th->ack && !paws_reject &&
            (after(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt) ||
             (tmp_opt.saw_tstamp &&
              (s32)(tcptw->tw_ts_recent - tmp_opt.rcv_tsval) < 0))) {
            u32 isn = tcptw->tw_snd_nxt + 65535 + 2;
            if (isn == 0)
                isn++;
            TCP_SKB_CB(skb)->tcp_tw_isn = isn;
            return TCP_TW_SYN;
        }
       	if (paws_reject)
    		__NET_INC_STATS(twsk_net(tw), LINUX_MIB_PAWSESTABREJECTED);

    	if (!th->rst) {
    		/* In this case we must reset the TIMEWAIT timer.
    		 *
    		 * If it is ACKless SYN it may be both old duplicate
    		 * and new good SYN with random sequence number <rcv_nxt.
    		 * Do not reschedule in the last case.
    		 */
    		if (paws_reject || th->ack)
    			inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN);

    		return tcp_timewait_check_oow_rate_limit(
    			tw, skb, LINUX_MIB_TCPACKSKIPPEDTIMEWAIT);
    	}

....
....
    
}

 

其中after宏的定义如下,  after(4119633515 , 693821462) ==> before(693821462,4119633515 )  ==> (__s32) (693821462 - 4119633515) = 869155243 **>0**,  因此 不满足after条件,因而 130 端回复了 旧连接的 ACK 报文, 由此分析 130 端回复重复的ACK是 Linux TCP 协议栈实现的正常行为。

/*
 * The next routines deal with comparing 32 bit unsigned ints
 * and worry about wraparound (automatic with unsigned arithmetic).
 */

static inline bool before(__u32 seq1, __u32 seq2)
{
        return (__s32)(seq1-seq2) < 0;
}
#define after(seq2, seq1) 	before(seq1, seq2)

 

 

## 结论

此问题由 NAT 网关 端口复用引起, 2MSL内复用端口,对TCP的特殊情况处理欠缺,导致报文投递存在问题,导致连接无法建立;需要在宿主机和NAT网关进一步分析报文未投递到云主机的原因。

参考代码: tcp_timewait_state_process

 

/*
 * * Main purpose of TIME-WAIT state is to close connection gracefully,
 *   when one of ends sits in LAST-ACK or CLOSING retransmitting FIN
 *   (and, probably, tail of data) and one or more our ACKs are lost.
 * * What is TIME-WAIT timeout? It is associated with maximal packet
 *   lifetime in the internet, which results in wrong conclusion, that
 *   it is set to catch "old duplicate segments" wandering out of their path.
 *   It is not quite correct. This timeout is calculated so that it exceeds
 *   maximal retransmission timeout enough to allow to lose one (or more)
 *   segments sent by peer and our ACKs. This time may be calculated from RTO.
 * * When TIME-WAIT socket receives RST, it means that another end
 *   finally closed and we are allowed to kill TIME-WAIT too.
 * * Second purpose of TIME-WAIT is catching old duplicate segments.
 *   Well, certainly it is pure paranoia, but if we load TIME-WAIT
 *   with this semantics, we MUST NOT kill TIME-WAIT state with RSTs.
 * * If we invented some more clever way to catch duplicates
 *   (f.e. based on PAWS), we could truncate TIME-WAIT to several RTOs.
 *
 * The algorithm below is based on FORMAL INTERPRETATION of RFCs.
 * When you compare it to RFCs, please, read section SEGMENT ARRIVES
 * from the very beginning.
 *
 * NOTE. With recycling (and later with fin-wait-2) TW bucket
 * is _not_ stateless. It means, that strictly speaking we must
 * spinlock it. I do not want! Well, probability of misbehaviour
 * is ridiculously low and, seems, we could use some mb() tricks
 * to avoid misread sequence numbers, states etc.  --ANK
 *
 * We don't need to initialize tmp_out.sack_ok as we don't use the results
 */
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);
	bool paws_reject = false;

	tmp_opt.saw_tstamp = 0;
	if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) {
		tcp_parse_options(twsk_net(tw), skb, &tmp_opt, 0, NULL);

		if (tmp_opt.saw_tstamp) {
			if (tmp_opt.rcv_tsecr)
				tmp_opt.rcv_tsecr -= tcptw->tw_ts_offset;
			tmp_opt.ts_recent	= tcptw->tw_ts_recent;
			tmp_opt.ts_recent_stamp	= tcptw->tw_ts_recent_stamp;
			paws_reject = tcp_paws_reject(&tmp_opt, th->rst);
		}
	}

	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;

		/* 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;

		/* FIN arrived, enter true time-wait state. */
		tw->tw_substate	  = TCP_TIME_WAIT;
		tcptw->tw_rcv_nxt = TCP_SKB_CB(skb)->end_seq;
		if (tmp_opt.saw_tstamp) {
			tcptw->tw_ts_recent_stamp = ktime_get_seconds();
			tcptw->tw_ts_recent	  = tmp_opt.rcv_tsval;
		}

		inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN);
		return TCP_TW_ACK;
	}

	/*
	 *	Now real TIME-WAIT state.
	 *
	 *	RFC 1122:
	 *	"When a connection is [...] on TIME-WAIT state [...]
	 *	[a TCP] MAY accept a new SYN from the remote TCP to
	 *	reopen the connection directly, if it:
	 *
	 *	(1)  assigns its initial sequence number for the new
	 *	connection to be larger than the largest sequence
	 *	number it used on the previous connection incarnation,
	 *	and
	 *
	 *	(2)  returns to TIME-WAIT state if the SYN turns out
	 *	to be an old duplicate".
	 */

	if (!paws_reject &&
	    (TCP_SKB_CB(skb)->seq == tcptw->tw_rcv_nxt &&
	     (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq || th->rst))) {
		/* In window segment, it may be only reset or bare ack. */

		if (th->rst) {
			/* This is TIME_WAIT assassination, in two flavors.
			 * Oh well... nobody has a sufficient solution to this
			 * protocol bug yet.
			 */
			if (twsk_net(tw)->ipv4.sysctl_tcp_rfc1337 == 0) {
kill:
				inet_twsk_deschedule_put(tw);
				return TCP_TW_SUCCESS;
			}
		} else {
			inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN);
		}

		if (tmp_opt.saw_tstamp) {
			tcptw->tw_ts_recent	  = tmp_opt.rcv_tsval;
			tcptw->tw_ts_recent_stamp = ktime_get_seconds();
		}

		inet_twsk_put(tw);
		return TCP_TW_SUCCESS;
	}

	/* Out of window segment.

	   All the segments are ACKed immediately.

	   The only exception is new SYN. We accept it, if it is
	   not old duplicate and we are not in danger to be killed
	   by delayed old duplicates. RFC check is that it has
	   newer sequence number works at rates <40Mbit/sec.
	   However, if paws works, it is reliable AND even more,
	   we even may relax silly seq space cutoff.

	   RED-PEN: we violate main RFC requirement, if this SYN will appear
	   old duplicate (i.e. we receive RST in reply to SYN-ACK),
	   we must return socket to time-wait state. It is not good,
	   but not fatal yet.
	 */

	if (th->syn && !th->rst && !th->ack && !paws_reject &&
	    (after(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt) ||
	     (tmp_opt.saw_tstamp &&
	      (s32)(tcptw->tw_ts_recent - tmp_opt.rcv_tsval) < 0))) {
		u32 isn = tcptw->tw_snd_nxt + 65535 + 2;
		if (isn == 0)
			isn++;
		TCP_SKB_CB(skb)->tcp_tw_isn = isn;
		return TCP_TW_SYN;
	}

	if (paws_reject)
		__NET_INC_STATS(twsk_net(tw), LINUX_MIB_PAWSESTABREJECTED);

	if (!th->rst) {
		/* In this case we must reset the TIMEWAIT timer.
		 *
		 * If it is ACKless SYN it may be both old duplicate
		 * and new good SYN with random sequence number <rcv_nxt.
		 * Do not reschedule in the last case.
		 */
		if (paws_reject || th->ack)
			inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN);

		return tcp_timewait_check_oow_rate_limit(
			tw, skb, LINUX_MIB_TCPACKSKIPPEDTIMEWAIT);
	}
	inet_twsk_put(tw);
	return TCP_TW_SUCCESS;
}
EXPORT_SYMBOL(tcp_timewait_state_process);

 

服务端返回了 旧的ACK, 导致 Client 发送了 RST 报文。 服务端收到 RST 后, 旧的socket将被kill,再到来的 SYN 可以建立新连接,此时重传的 SYN 在服务端应该正常响应 SYN+ACK, 因此服务端继续回复 SYN + ACK 的行为是符合预期的正常行为。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
1
0