Linux中,SCTP是作为一个kernel模块编译加载的,代码主要是在net/sctp目录下。
1. 主要数据结构
1.1 endpoint
struct sctp_endpoint{
struct list_head asocs;
……
}
在应用程序调用listen()之后,会创建一个endpoint结构。收到INIT CHUNK的时候,如果没有找到相应的endpoint,则association的建立请求会被拒绝。
1.2 association
struct sctp_association {
struct sctp_ep_commonbase;
...
struct {
...
struct list_head transport_addr_list;
...
}peer;
...
}
association结构保存了一条SCTP连接(association)的各种参数,以及对端(peer/path)的地址列表。
1.3 path
struct sctp_transport {
…
}
保存了peer/path的参数信息,即association结构中transport_addr_list链表中的各个节点。
2. SCTP状态机
linux中SCTP协议的实现是采用状态机的方式,状态机定义了各种“状态”下对各种“事件”的处理方法。
SCTP定义了以下状态:
事件有以下几类:
在sm_statetable.c中,定义了以下状态机:
3. SCTP收包处理
在收到一个SCTP包之后,以CHUNK为单位,一个一个的处理。大致流程如下:
association建立起来之后,会加到一个hash表。在收到一个SCTP包的时候,通过关键字(源IP地址、目的IP地址、源SCTP端口号、目的SCTP端口号)匹配来查找对应的association。然后sctp_do_sm()函数会根据当前ASSOCIATION的状态以及CHUNK类型找到处理函数。
4. SCTP发包
发包流程如下:
5. SCTP查表的性能问题
在LINUX 4.5之前,是用association来组织hash表。由于同一条association的两端都可以用多个IP地址,因此只能用两端的端口号作为关键字来组织association的hash表。此时,在所有association的两端端口号都相同的情况下,所有association都会被加入到hash表的同一条链表上。这种情况下,在查表的时候,有可能要遍历比较所有的association。在收到INIT处理建立请求的时候尤其严重,必须得遍历所有association,判断是否已经存在相同的association。
在LINUX 4.5,不是把association加入hash表,而是把struct sctp_transport加入hash表。通过查transport来得到SCTP包所属的association。由于是以transport来组织hash表,因此对端IP地址(association中,每个对端IP地址有一个唯一的transport)、端口号都可以用来计算hash表的key(即hash数组的下标)。即使所有association的端口号都相同,由于对端IP地址不相同,也可以分散到hash表,基本上满足各种场景下的性能需求。