随着IP网络业务的发展,尤其是VoIP的发展,TCP出现了很多局限性,例如对于VoIP信令及异步基于事务应用的处理。因此,IEFT的信令传输工作组(SIGTRAN)提出了一种面向多媒体通信的流控制传输协议(SCTP),用于在IP网络上传输PSTN信令消息,即通常所说的SS7overIP。它把SS7信令网络的一些可靠特性引入到了IP。
- SCTP是一种可靠的传输协议。SCTP提供的服务很像TCP,但同时又结合了UDP的一些优点。它提供了可靠、高效、有序的数据传输服务。相比之下TCP是面向字节的,而SCTP是针对成帧的消息(这点是类似于UDP的)。
- SCTP又是一个面向连接的传输协议,其连接被称为association即耦联,SCTP的“耦联”比TCP连接更为广泛:TCP的连接的每一端只有一个源地址和一个目的地址,而SCTP耦联的每一端能为另一端提供一组传输地址。
SCTP可以看做是TCP的增强协议,因为其可靠传输和拥塞控制机制基本都来自于TCP,但是它又对TCP进行了增强和完善,使得信令传输具有更高的可靠性。与TCP相比,SCTP最大的改变是增加了对多宿主(Multi-homing)、多流(Multi-streaming)以及部分有序(partialordering)的支持。
一、SCTP报文格式
SCTP头部格式如图所示:
一个SCTP包含了一个公共的报文头(CommonHeader)和若干数据块(Chunk),每个数据块中既可以包含控制信息,也可以包含用户数据。除了INIT、INITACK和SHUTDOWNCOMPLETE数据块外,其他类型的多个数据块可以捆绑在一个SCTP报文中,以满足对MTU大小的要求。当然,这些数据块也可以不与其他数据块捆绑在一个报文中。如果一个用户消息不能放在一个SCTP报文中,这个消息可以被分成若干个数据块。
公共头的各个部分及其含义:
- 源端口号和目地端口号:用于STCP的多路复用和多路分解,即标识发端和收端应用进程。
- 验证标签:耦联建立时,本端为这个偶联生成一个随机标识。偶联建立过程中,双方会交换这个TAG,到了数据传递时,发送端必须在公共报文头中带上对端的这个TAG,以备校验。
- 校验码:用于数据完整性校验。发送端产生,接收端验证。
数据块的各个部分及其含义:
- 块类型:块类型定义在块值(ChunkValue)中消息所属的类型。如果接收端点不能识别块类型时,块类型最高位2bit用于标识需要进行的各种操作。此时最高两位含义如下:
- 停止处理并丢弃此SCTP报文,不再处理该SCTP报文中的其他消息块。
- 停止处理并丢弃此SCTP报文,不再处理该SCTP报文中的其他消息块,并且在“ERROR”或“INITACK”中向发起端点返回不能识别的参数。
- 跳过此数据块并继续执行。
- 跳过此数据块并继续执行,并且在“ERROR”或“INITACK”中向发起端点返回不能识别的参数。
- 数据块标志位:块标志位用法由块类型决定。
- 块长度:块长度包括块类型(ChunkType)、块标记(ChunkFlags)、块长度(ChunkLength)和块值(ChunkValue),长度使用二进制表示。
- 块值:该块包含的数据内容。
二、STCP的多路复用和解复用
SCTP的多路复用和解复用采用了和TCP类似的机制,STCP也使用源IP+源端口+目地IP+目地端口的四元组来标识一个socket。但是需要注意的是SCTP支持mutihome,因而源IP和目地IP都不是唯一的,而允许是连接建立过程中通告的多个IP地址中的任意一个。
在STCP工作时,无论发送端还是接收端,都必须首先建立连接,在连接建立完成后,socket即拥有了它所需的四元组的信息,之后的收发都经过socket进行。
三、SCTP的状态迁移图
SCTP的状态迁移图如下所示:
该状态机详细显示了SCTP的状态以及它们之间的迁移过程。
四、SCTP的连接管理
1.建立连接
不同于TCP,SCTP通过四次握手来完成连接的建立:
- 连接发起者(一般为客户端)SCTP发送一个INIT消息(初始化)。该消息包括了连接发起者的IP地址清单、初始序列号、用于标识本耦联中所有报文的起始标记、客户请求的外出流的数目以及客户能够支持的外来流的数目
- 对端(服务器)发送一个INITACK消息确认连接发起者的INIT消息,其中含有服务器的IP地址清单、初始序列号、起始标记、服务器请求的外出流的数目、服务器能够支持的外来流的数目以及一个状态cookie,状态cookie包含服务器用于确信本耦联有效所需的所有状态,cookie是经过数字签名的,因而可以确保其有效性
- 客户以一个COOKIEECHO消息返回服务器的状态cookie,除COOKIEECHO外,该消息可能在同一个报文中捆绑一个用户数据
- 服务器以一个COOKIEACK消息确认客户返回的cookie是正确的,到此时该耦联就建立成功了。该消息也可能在同一个报文中捆绑一个用户数据。
其图示如下:
与TCP的连接建立过程相比:
- SCTP经过四次握手才能建立一个耦联。
- INIT除了包括了一个类似于TCP的ISN的一个初始序列号之外,还包括了一个验证标记Ta。Ta是一个验证标记,此后对端所有返回的数据中都必须包含这个验证标记
- INITACK中的Tz验证标记类似于INIT消息中的Ta标记,连接发起者之后的所有数据都必须包含Tz这个验证标记。
- INIT接收端还在作为响应的INITACK中提供一个cookieC,包含设置本SCTP耦联所需的所有状态,用于防止“Dos”攻击。
在一次SCTP四次握手中,INIT消息的接收端不必保存任何状态信息或者分配任何资源,这样就可防范SYNFlooding等DoS攻击。它在发送INIT-ACK消息时,采用了一种机制——“状态Cookie”,该Cookie具有发送端要建立自己状态所需的全部信息。
用于建立连接的INIT ACK只能在COOKIE WATI状态收到,在其它状态收到该报文时都会直接丢弃,类似的,COOKIE ACK只能在COOKIE ECHOED状态接收。
在常规的握手中,主动发起方的本地tag在发起握手时产生,主动发起方的对端tag在收到INIT ACK时产生。而连接的被动方的本地tag和对端tag都在收到INIT时产生,但是最终要到收到了COOKIE ECO后才确定并保存下来。
SCTP产生一个状态Cookie的过程如下:
- 使用收到的INIT和发出的INIT-ACK块中的信息创建一个关联的TCB(传输控制块)。
- 在TCB中,将当前日期设为创建日期,将协议参数“有效Cookie时间”设为生存期间。
- 根据TCB,收集重建TCB所需的最小信息子集,将该子集和密钥产生一个MAC(信息认证编码)。
- 结合上述最小信息子集和MAC产生状态Cookie。
- 在发送完INITACK(包含状态Cookie参数)后,发送方必须删除TCB以及任何与新关联有关的本地资源。
INIT和INIT-ACK都必须包含建立初始状态所需的参数:一组IP地址,保证可靠传输的初始序列号,每个被接收的SCTP报文中必须含有的验证标签,每一端请求发出的流数目和每一端能支持接收的流数目。交换完这些消息之后,INIT的发送端以COOKIE-ECHO消息的方式发送回状态Cookie。接收端根据所接收到的COOKIE-ECHO中的状态Cookie,完整地重建自己的状态,并回送COOKIE-ACK来确认关联已建立。
因此对于SCTP,即使接收再多的INIT消息,接收端也没有任何资源的消耗:它既不分配任何系统资源,也不保存此次新关联的状态,它只是把相应重建状态所用的状态Cookie作为参数,包含在每一个回送的INIT-ACK消息中,最后该状态Cookie会被COOKIE-ECHO消息发送回来。
类似于TCP,SCTP也多由客户端执行主动打开,而服务器执行被动打开。
2.终止连接
与TCP不同,SCTP使用三次握手来关闭一个耦联。而且SCTP不支持TCP所支持的“半关闭”状态。典型的SCTP关闭一个耦联的过程如下:
- 应用程序发出关闭请求,SCTP耦联进入SHUTDOWN-PENDING状态,并且不再接收应用程序的数据,只发送队列中还未发送的数据,再队列中没有待发送数据后,发送SHUTWODN并进入SHUTDOWN-SENT状态。这一方被称为主动关闭。
- 执行被动关闭的一方在接收到主动关闭一方的SHUTWODN消息时,进入SHUTDOWN-RECEIVED状态,此时执行被动关闭一方不再接受上层应用的数据,只发送队列中剩余的数据。在发送队列中的数据被发送完后,执行被动关闭一方发送SHUTDOWN-ACK并进入SHUTDOWN-ACK-SENT状态。
- 执行主动关闭一方收到SHUTDOWN-ACK后就发送SHUTDOWN-COMPLETE,并进入CLOSE状态。
- 执行主动关闭一端接收到SHUTDOWN-COMPLETE后就进入close状态。
其图示如下:
和TCP不同,由于SCTP的报文中带有验证标记,因而不会出现将具有相同的四元组上的旧的耦联的报文当做新耦联的报文的情形,因而SCTP关闭时不存在TIME_WAIT状态。
3.同时打开
RFC规定,如果SCTP在COOKIE-WAIT或者COOKIE-ECHOED状态接收到INIT报文。则:
- INIT报文的接收者产生一个INIT-ACK,该INIT-ACK使用的本端参数和自己发送的那个INIT报文的相同
- 执行状态COOKIE的计算过程,产生一个状态COOKIE
- 不允许修改SCTP的状态
- 状态COOKIE相关的TCB不能删除
- 不关闭T1-init定时器
如果SCTP在非COOKIE-WAIT状态接收到了INIT-ACK,则丢弃它。
则同时打开可能如下图所示(是5.2.4的case D):
根据INIT-ACK的相关处理规则,只要在COOKIE-WAIT状态收到了INIT-ACK,则SCTP就会发送COOKIE-ECHO并进入COOKIE-ECHOED状态。因而在上图的“同时打开”场景下,SCTP会走到图中所示的状态,之后的处理则会有所不同。
在这种场景中,收到COOKIE-ECHO后会从其中恢复TCB信息,并将其和在发送INIT时产生的那个没有被删除的TCB的信息进行比较,如果本端和对端的tag信息都相同(在这种场景中,这是我们期望的),则会直接进入ESTABLISHED状态,其它情形的处理可参考RFC 2960的5.2.4一节,该节给出了详细的处理规则。
更复杂一点的同时打开可能如下所示(图画的不是特别好,在ubuntu下画的。5.2.4的case A很简单,当连接建立后一端down掉,然后用一致的参数尝试建立连接就是该情形。):
4.同时关闭
极少数情况下,耦联的双发可能同时执行主动关闭,即同时进入发送SHUTWODN并进入SHUTDOWN-SENT状态。在这种情况下关闭的流程为:
- 两端都发送SHUTWODN并进入SHUTDOWN-SENT状态
- 两端都收到对方的SHUTDOWN消息,并发送SHUTDOWN-ACK,然后进入SHUTDOWN-ACK-SENT状态
- 两端都收到对方的SHUTDOWN-ACK,并发送SHUTDOWN-COMPLETE,然后就进入close状态
五、和TCP的区别
SCTP可以看做是对TCP进行了增强的传输层协议,和TCP的不同包括:
- 支持多宿主:SCTP的每一端都可以提供多个IP地址,对于对端提供的每个IP地址,SCTP都会通过HEARD_BEAT机制来检测其可达性,当其中一个IP地址由于网络故障而不可达时,SCTP会将报文送到一个可达的地址。通过这种方式,SCTP提高了可靠性,可以容忍网络级错误。
- 支持多种传输模式:除了支持TCP的严格有序传输完,SCTP还支持部分有序传输和无序传输(像UDP)。
- 支持多流:TCP以字节流的方式工作,通过TCP连接传输的数据以字节流的方式在TCP连接的两端之间流动。但是在SCTP中,流用来指示需要按顺序递交到上层协议的用户消息的序列,在同一个流中的消息需要按照其顺序进行递交。严格地 说,“流”就是一个SCTP 偶联中,从一个端点到另一个端点的单向逻辑通道。一 个偶联是由多个单向的流组成的。各个流之间相对独立,使用流ID 进行标识,每个 流可以单独发送数据而不受其他流的影响。
- 路径管理:SCTP 路径管理功能主要负责从远端提供的一组传输地址中选择目的传输地址,它根据SCTP用户的指令和当前可达的目的地来选择目的地址。在SCTP建立耦联时,会向对端通告本端可用的所有地址,并且会获取对端的所有可用地址。随后SCTP会通过HEART_BEAT定时检测对端所有地址的可达性。
- 选择性确认(SACK):在TCP中其确认机制的基础是累积确认,SACK是选项,不一定启用,但是SCTP中是必须使用的。SCTP使用SACK反馈给发送端的是丢失的并且要求重传的消息序号。
- 防范拒绝服务(DoS)攻击:SYN Flooding攻击是DoS的一种方式,TCP的实际实现中通过扩展实现了对它的防御,但是在SCTP协议中已经增加了对它的防御。方法是在耦联初始化阶段实施一种安全的“Cookie”机制。
- 根据已发现的路径MTU(最大传输单元)大小进行用户数据分片: TCP根据MSS确定发送给对端的最大字节长度,MSS可能来自于路径MTU发现也可能不是来自于路径MTU发现。但是对于SCTP,为了确保发送到下层的SCTP数据包与路径MTU一致进而避免分片,SCTP对用户消息分片。在接收端,分片被重组后传给上层SCTP用户。
- 验证标签:SCTP 报文的公共分组头包含一个验证标签(VerificATIon Tag)。 验证标签的值由偶联两端在偶联启动时选择。如果收到的分组中如果没有期望的验证标签值,接收端将丢弃这个分组,以阻止攻击和失效的SCTP 分组。这个特性也使得SCTP可以区分SCTP报文段是属于本耦联的还是本耦联所用的四元组的上一个实例的。
- 消息块绑定:类似于TCP当用户数据很短时,数据传输的效率会很低。为了应对该问题,SCTP会将几个用户数据绑定在一个SCTP 报文里面传输,以提高带宽的利用率。用户可以选择是否启用该功能,但是当出现拥塞/重传时即便用户选择不使用该功能,该功能也仍会被使用。相比之下TCP需要打开TCP_CORK 选项的支持才能获得类似的功能。