博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux 协议栈tcp的rst报文中,seq的选取问题
阅读量:7242 次
发布时间:2019-06-29

本文共 3658 字,大约阅读时间需要 12 分钟。

之前在《深入理解并行编程》的群里,有个小米的兄弟问了一个问题,服务器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.

 如果不是rst的方式结束,而是fin的方式结束,那么这个seq则应该为多少呢?

我们来关注下 tcp_send_fin 函数,看看它怎么使用seq的,

void tcp_send_fin(struct sock *sk){	struct tcp_sock *tp = tcp_sk(sk);	struct sk_buff *skb = tcp_write_queue_tail(sk);	int mss_now;	/* Optimization, tack on the FIN if we have a queue of	 * unsent frames.  But be careful about outgoing SACKS	 * and IP options.	 */	mss_now = tcp_current_mss(sk);	if (tcp_send_head(sk) != NULL) {//说明还有空间,tcp_send_head返回值为sk->sk_send_head,		TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_FIN;		TCP_SKB_CB(skb)->end_seq++;		tp->write_seq++;	} else {		/* Socket is locked, keep trying until memory is available. */		for (;;) {			skb = alloc_skb_fclone(MAX_TCP_HEADER,					       sk->sk_allocation);//申请一个skb			if (skb)				break;			yield();		}		/* Reserve space for headers and prepare control bits. */		skb_reserve(skb, MAX_TCP_HEADER);		/* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */		tcp_init_nondata_skb(skb, tp->write_seq,				     TCPHDR_ACK | TCPHDR_FIN);//fin包占用一个seq		tcp_queue_skb(sk, skb);	}	__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_OFF);}

  不管有没有空间,其实发送出去的序列号就是当前报文的下一个,因为fin也占用一个seq,所以这个seq也是上次发完的seq+报文长度+1.

转载于:https://www.cnblogs.com/10087622blog/p/9340712.html

你可能感兴趣的文章
关于root(其他)用户拒绝登陆mysql的处理方法
查看>>
Linux基本常用命令总结-初级
查看>>
域计算机修改修改本地帐号密码
查看>>
负载均衡集群的实现方式之一LVS
查看>>
公有云产品试用介绍
查看>>
我的友情链接
查看>>
Servlet+JSP+MySQL实现用户管理模块之六、实现用户信息显示
查看>>
软件项目管理
查看>>
3012.脚本作业—l201.10.0编写一个脚本用于检测IP地址(递进版10)
查看>>
rpmbuild SPEC文件
查看>>
心在山水间
查看>>
ionic开发android app步骤
查看>>
【数据结构】位图BitMap与布隆过滤器BloomFilter
查看>>
mysql主从 主主
查看>>
Java中FileInputStream和FileOutputStream类实现文件夹及文件的复制粘贴
查看>>
tomcat+jdk部署
查看>>
Toast源码深度分析
查看>>
zabbix监控公网机器
查看>>
python requests模块详解
查看>>
PHP应用架构演化
查看>>