linux 协议栈tcp的rst报文中,seq的选取问题

Posted 安庆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux 协议栈tcp的rst报文中,seq的选取问题相关的知识,希望对你有一定的参考价值。

之前在《深入理解并行编程》的群里,有个小米的兄弟问了一个问题,服务器A发包给服务器B,Seq是1,但是在未能收到服务器B的报文回复的情况下,发送了
rst,但是rst报文中,对应的seq是1461,一堆人都在猜测,为什么seq跳变了,由于当时只看到一半的图片,所以我让他发送完整报文出来之后,我

发现其实rst的seq不是1的原因,并不是因为跳变,而是正常的,因为发送给B的报文,长度为1460,但是这个报文没有得到回复,所以在超时之后,应用程序关闭了这条连接,

导致内核协议栈发送了一个rst报文,而rst报文选取seq的时候,并不是选取的确定已经发送的seq,而是当前连接已经用掉的seq,也就是当前seq,哪怕这个报文没有收到回复,也会使用。

具体看代码:

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

	/* 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);//注意传入的标志是rst,不是fin,可以具体参考tcp_send_fin 是怎么传参数的
	/* Send it off. */
	if (tcp_transmit_skb(sk, skb, 0, priority))
		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED);

	TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);
}

  其中关注下报文的init过程:

/* Constructs common control bits of non-data skb. If SYN/FIN is present,
 * auto increment end seqno.
 */
static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags)
{
	struct skb_shared_info *shinfo = skb_shinfo(skb);

	skb->ip_summed = CHECKSUM_PARTIAL;
	skb->csum = 0;

	TCP_SKB_CB(skb)->tcp_flags = flags;
	TCP_SKB_CB(skb)->sacked = 0;

	shinfo->gso_segs = 1;
	shinfo->gso_size = 0;
	shinfo->gso_type = 0;

	TCP_SKB_CB(skb)->seq = seq;
	if (flags & (TCPHDR_SYN | TCPHDR_FIN))//我们本文的标志是 TCPHDR_ACK | TCPHDR_RST ,
            seq++;//此处+1 ,但进不来
TCP_SKB_CB(skb)->end_seq = seq;//所以本文应该是传入seq是多少就发送多少 }

  那么传入的seq是多少呢?

/* SND.NXT, if window was not shrunk.
 * If window has been shrunk, what should we make? It is not clear at all.
 * Using SND.UNA we will fail to open window, SND.NXT is out of window. :-(
 * Anything in between SND.UNA...SND.UNA+SND.WND also can be already
 * invalid. OK, let‘s make this for now:
 */
static inline __u32 tcp_acceptable_seq(const struct sock *sk)
{
	const struct tcp_sock *tp = tcp_sk(sk);

	if (!before(tcp_wnd_end(tp), tp->snd_nxt))
		return tp->snd_nxt;
	else
		return tcp_wnd_end(tp);
}

  注释写得比较清楚,如果窗口没有shrunk,也就是tp->snd_nxt 没有out of window 的话,则取得就是tp->snd_nxt,而这个值,就是报文长度+1了,也就是1461.

 



以上是关于linux 协议栈tcp的rst报文中,seq的选取问题的主要内容,如果未能解决你的问题,请参考以下文章

Linux 网络协议栈开发基础篇(十三)——TCP/IP协议各报文头格式

Linux 网络协议栈开发基础篇(十三)——TCP/IP协议各报文头格式

Linux2.6内核协议栈系列--TCP协议2.接收

TCP协议的三次握手和四次挥手

:传输层协议介绍

Linux 网络协议栈开发基础篇—— 使用wireshark分析TCP/IP协议中TCP包头的格式