TCP协议:SYN、ACK、ISN、SeqNumber
0. 前言
在网络分析中,读懂TCP序列号和确认号的值,可以帮助我们学习TCP协议以及排查问题,如通过查看序列号和确认号可以确定数据传输是否乱序。
在网络分析中,读懂TCP序列号和确认号的值,可以帮助我们学习TCP协议以及排查问题,如通过查看序列号和确认号可以确定数据传输是否乱序。
SYN:同步标志 同步序列编号(Synchronize Sequence Numbers)栏有效。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。在这里,可以把 TCP序列编号看作是一个范围从0到4,294,967,295的32位计数器。通过TCP连接交换的数据中每一个字节都经过序列编号。在TCP报头中的序列编号栏包括了TCP分段中第一个字节的序列编号。
ACK:确认标志 确认编号(Acknowledgement Number)栏有效。大多数情况下该标志位是置位的。TCP报头内的确认编号栏内包含的确认编号(w+1,Figure-1)为下一个预期的序列编号,同时提示远端系统已经成功接收所有数据。
TCP协议工作在传输层,是一种可靠的面向连接的数据流协议。TCP之所以可靠,是因为它保证了传送数据包的顺序。顺序是用一个序列号来保证的。响应包内也包括一个序列号,表示接收方准备好这个序列号的包。在TCP传送一个数据包时,它会把这个数据包放入重发队列中,同时启动计时器,如果收到了关于这个包的确认信息,便将此数据包从队列中删除,如果在计时器超时的时候仍然没有收到确认信息,则需要重新发送该数据包。另外,TCP通过数据分段中的序列号来保证所有传输的数据可以按照正常的顺序进行重组,从而保证数据传输的完整。
TCP通讯中主要有连接的建立、数据的传输、连接的关闭三个过程。每个过程完成不同的工作,而且序列号和确认号在每个过程中的变化都是不同的。
TCP建立连接,也就是三路握手,需要三步完成。发送第一个SYN的一端执行的是主动打开。而接收这个SYN并发回下一个SYN的另一端执行的是被动打开。过程大致如下:
1. 客户端向服务器发送一个同步数据包请求建立连接,该数据包中,初始序列号(ISN:Inital Sequence Number)是客户端随机产生的一个值,确认号是0;
2. 服务器收到这个同步请求数据包后,会对客户端进行一个同步确认。这个数据包中,序列号(ISN)是服务器随机产生的一个值,确认号是客户端的初始序列号+1;
3. 客户端收到这个同步确认数据包后,再对服务器进行一个确认。该数据包中,序列号是上一个同步请求数据包中的确认号值,确认号是服务器的初始序列号+1。
1. 为什么要初始化序列号(ISN)
在前面学习tcp连接三次握手的时候,客户端和服务端在建立tcp连接时,双方都会发送SYN报文并初始化序号(英文为:Initial Sequence Number,简称ISN)。大家不妨先思考一下:为什么要在建立tcp连接时初始化序列号?如果双方在建立tcp连接时使用相同的序号又会有什么问题?
2. 使用固定序列号的问题
带着这几个问题,我们通过这个例子来看一下在tcp连接建立时使用相同序号的情况,如下图所示:
1. 假设现在A和B使用固定的序号,A使用序号1和B建立tcp连接,发送一个SYN报文,SEQ = 1,经过三次握手后A和B之间就建立tcp连接完成。
2. 当A和B建立完tcp连接后,然后A又使用相同的序号1向B发送了200字节数据,不过因为网络拥塞问题,这个tcp数据报一直在网络中逗留,并没有立即到达B。
3. 由于A发送的tcp数据报一直没到达B,正好此时A发生故障并重启,于是B就释放这条tcp连接了。
4. 然后A重启后又使用序号1和B建立新的tcp连接。
5. 当tcp连接建立完成后,A又使用序号1向B发送了240字节的数据,不过这次网络很稳定,B马上就收到A发送的这240字节的数据,注意:此时A发送数据是使用的新的tcp连接。
6. 然后A之前发送的tcp数据报(200字节)经过一段时间后,终于到达B。不过这个tcp数据报是属于之前已经释放的旧的tcp连接,按理来说,B应该把这个数据报丢弃掉才是。但是由于A每次发送报文都使用了相同的序号(SEQ = 1),这可能会让B误认为这200字节是属于新建立的tcp连接的,因此B会对这200字节数据照收不误。这就导致B在收到新tcp连接的数据后,又收到旧tcp连接的数据,从而出现数据乱序的问题。
3. 初始化序列号的目的
由于A和B之间的一个tcp连接通常是由A和B的2个ip地址,2个端口号构成的四元组,因此当A出现了故障把这个tcp连接断开了,之后再以相同的四元组建立新的tcp连接(也就是说A和B两次建立tcp连接都是使用了相同的ip地址和端口),就会出现数据乱序的问题。
换句话说,只要A发送了一个tcp报文段,且这个tcp报文段的四元组和序号,和之前的tcp连接(四元组和序号)相同的话,就会被B确认。这其实反映了tcp的一些缺点,如果被一些恶意攻击者加以利用tcp的这种缺点:选择合适的序号,ip地址和端口的话,就能伪造出一个tcp报文段,从而打断正常的tcp连接。但是初始化序号的方式(通过算法来随机生成序号)就会使序号难以猜出,也就不容易利用这种缺点来进行一些恶意攻击行为。
通过上面所述我们知道,如果A和B之间发送数据每次都使用相同序号的话可能会引发一系列的问题,但是使用不同序号的话,那么B在接收到这个序号为1的tcp报文时,发现这个tcp报文的序号不在新tcp连接的接收范围内时会把这个tcp报文丢弃掉,也就避免了数据乱序的问题。
因此我们可以明白,客户端和服务端双方在建立tcp连接并初始化序列号,那么上面所说的这些情况从一开始就可以避免。另外,tcp在初始化序列号的过程也是比较复杂的,一般来说,这个序号的范围是
−1之间,而且序号的生成也是随机的,通常是一个很大的数值,也就是说每个tcp连接使用的序号也是不一样的。
4. ISN生成算法
TCP初始化序列号不能设置为一个固定值,因为这样容易被攻击者猜出后续序列号,从而遭到攻击。
RFC1948中提出了一个较好的初始化序列号ISN随机生成算法。
ISN = M + F(localhost, localport, remotehost, remoteport).
M是一个计时器,这个计时器每隔4毫秒加1。
F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。要保证Hash算法不能被外部轻易推算得出,用MD5算法是一个比较好的选择。
ISN会和一个假的时钟绑在一起,这个时钟M会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始。这样,一个ISN的周期大约是4.55个小时。因为,我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime(缩写为MSL – Wikipedia语条),所以,只要MSL的值小于4.55小时,那么,我们就不会重用到ISN。
序号(ISN)在tcp中是非常重要的,学好tcp的第一步就是先把序号(ISN)搞懂。
参考文献:
https://blog.csdn.net/feng125452/article/details/33347169