这一章,我们将了解SCTP数据包结构,并简要介绍SCTP协议,最后将带有多个chunk的SCTP包拆分问单个SCTP数据包。
SCTP数据包
数据包头
|
Eth信息
|
IP头
|
SCTP头
|
SCTP Chunk 1
|
……
|
SCTP Chunk n
|
其中数据包头和IP头已经在前面做过介绍了,这里先简单介绍一下Eth信息。
-
- typedef struct__Ethernet_Info
- {
- Byte DestMac[6];
- Byte SrcMac[6];
- _Int16 iType;
-
- } __EthernetInfo;
DsetMac 目的主机mac地址
SrcMac 源主机MAC地址
Type 协议类型,IP为0x0800
SCTP简介

一个 SCTP
分组含了一个公共的分组头(Common
Header)和若干数据块(Chunk),每个数据块中既可以包含控制信息,也可以包含用户数据。除了INIT、INIT ACK和SHUTDOWN
COMPLETE 数据块外,其他类型的多个数据块可以捆绑在一个SCTP 分组中,以满足对 MTU
大小的要求。当然,这些数据块也可以不与其他数据块捆绑在一个分组中。如果一个用户消息不能放在一个SCTP 分组中,这个消息可以被分成若干个数据块。
SCTP头
SCTP 公共分组头中包括了源端口号(Source Port Number)、目的端口号(Destination PortNumber)、验证标签(Verification Tag)和校验码(Checksum)
1.源端口号(16 bits)
源端口号识别 SCTP 发送端点的SCTP 端口号。接收方可以使用源端口号、源IP 地址、目的端口号和目的IP 地址标识该SCTP 分组所属的偶联。
2.目的端口号(16 bits)
目的端口号为目的端点的 SCTP 端口号。接收主机可以使用目的端口号将SCTP 分组解复用到正确的端点或应用中。
3.验证标签(32 bits)
验证标签是偶联建立时,本端端点为这个偶联生成一个随机标识。偶联建立过程中,双方会交换这个TAG,到了数据传递时,发送端必须在公共分组头中带上对端的这个TAG,以备校验。
4.校验码(32 bits)
SCTP 通过对用户数据使用ADLER-32 算法,计算出一个32 位的校验码,带在数据报中,在接收端进行同样的运算,通过检查校验码是否相等来验证用户数据是否遭到破坏。
数据块
数据块包括了块类型(Chunk Type)、块标志位(Chunk Flags)、块长度(Chunk Length)和块值(Chunk Value )。
1.块类型(8 bits)
块类型定义在块值(Chunk Value)中消息所属的类型。
0 |
DATA(净数据) 传输的用户数据块。 |
1 |
INIT 用于发起两个端点之间的SCTP 偶联。 |
2 |
INIT ACK 用来确认SCTP 偶联的发起消息(INIT)。 |
3 |
SACK 该数据块送至对端,以确认收到DATA 块,并且通知对端DATA 的接收顺序间隙。 |
4 |
HEARTBEAT 端点发送该数据块至对端,以检测当前偶联中定义的某一目的地址的可达性。 |
5 |
HEARTBEAT ACK 响应HEARTBEAT 消息。 |
6 |
ABORT 关闭偶联。 |
7 |
SHUTDOWN 偶联中的一个端点对其偶联发起一个GRACEFUL关闭。 |
8 |
SHUTDOWN ACK 响应SHUTDOWN 消息,关闭程序完成时发出。 |
9 |
ERROR 通知对端,SCTP 偶联发生某种错误。 |
10 |
COOKIE ECHO 仅用于偶联发起过程,它由偶联的发起者发送至对端以完成发起程序。 |
11 |
COOKIE ACK COOKIE 证实,相对于COOKIE ECHO |
12 |
ECNE 保留,应用于外部环境拥塞发布回声 |
13 |
CWR 保留,应用于降低拥塞窗口 |
14 |
SHUTDOWN COMPLETE用于关闭程序完成时对SHUTDOWN ACK 消息进行确认 |
15-62 |
IETF 保留 |
63 |
IETF 定义块扩展使用 |
64-126 |
IETF 保留 |
127 |
定义块扩展使用 |
128-190 |
IETF 保留 |
191 |
定义块扩展使用 |
192-254 |
IETF 保留 |
255 |
IETF 定义块扩展使用如果接收端点不能识别块类型时,块类型最高位2bit 用于标识需要进行的各种操作。 |
Bits(最高两位) 含义
00 |
停止处理并丢弃此SCTP 分组,不再处理该SCTP 分组中的其他消息块。 |
01 |
停止处理并丢弃此SCTP 分组,不再处理该SCTP 分组中的其他消息块,并且在“ERROR”或“INIT ACK”中向发起端点返回不能识别的参数。 |
10 |
跳过此数据块并继续执行。 |
11 |
跳过此数据块并继续执行,并且在“ERROR”或“INIT ACK”中向发起端点返回不能识别的参数。 |
2.数据块标志位(8bit)
块标志位用法由块类型决定。除非被置为其他值,块标记在传送过程中会被置0 而且接收端点会忽视块标记。
定义见:HTTP:\\
3.块长度(16bit)
块长度包括块类型(Chunk Type)、块标记(Chunk Flags)、块长度(Chunk Length)和块值(Chunk Value),长度使用二进制表示。
4.块值(可变长度)
块值的内容在块中传送实际的信息,内容由消息块类型决定。块值的长度为不定长。
SCTP结构体定义
-
- typedef struct __Sctp_header
- {
- _Int16 SrcPort;
- _Int16 DstPort;
-
- _Int32 iVerTag;
- _Int32 iChecksum;
- } __SctpHeader;
-
-
- typedef struct __Sctp_chunk_header
- {
- Byte Type;
- Byte Flag;
- _Int16 iLength;
- } __SctpChunkHeader;
-
-
- typedef struct __Sctp_chunk
- {
- __SctpChunkHeader header;
- Byte* pData;
- } __SctpChunk;
拆分SCTP数据块
下面的代码将逐个解析数据包,当数据包位SCTP包时,对DATA chunk进行拆分。
- bool main()
- {
- __pcap_header header;
- int iNo = 1;
-
-
- if( !OpenPcapFile( "sctp.pcap")|| !OpenOutFile( "export.pcap"))
- {
- return false;
- }
-
-
- GetPcapHeader( &header);
-
- WriteFileHeader( &header);
-
- MoveFirst();
- while( !IeEof())
- {
- __pk_header data;
- __ip_header ipData;
- __EthernetInfo ethInfo;
- Byte* pBuffer;
-
- GetPacketAndMoveNext( &data,&pBuffer);
-
- GetEthernetInfo( eInfo, pBuffer,0);
-
- GetIpData( &ipData, pBuffer,sizeof(__EthernetInfo));
-
-
- if( ipData.byteProtocol == 132)
- {
-
- int iOffset = sizeof(__EthernetInfo) + ipData.byteHdLength * 4;
- int iChunkOffset =iOffset + sizeof( __SctpHeader);
- __SctpHeader sctpHeader;
- __SctpChunksctpChunkArr[MAX_CHUNK_NUM];
-
- int iChunkNum = 0;
-
- int iLenght = 0;
-
- GetSctpHeader(&sctpHeader, pBuffer, iOffset);
-
- while( true)
- {
-
- __SctpChunksctpChunk;
-
- int i = 0;
- GetSctpChunk(&sctpChunk, pBuffer, iChunkOffset);
-
- if(sctpChunk.header.Type == 0)
- {
- WritePkHeader(&data, iLenght + sctpChunk.header.iLength + LENGTH_SCTPALLHEADER(ipData));
- WriteEthInfo(eInfo);
- WriteIpHeader(&ipData, iLenght + sctpChunk.header.iLength + LENGTH_SCTPIPHEADER(ipData));
- WriteSctpHeader(&sctpHeader);
- for( i =0; i < iChunkNum; i++)
- {
- WriteSctpChunk(sctpChunkArr + i);
- }
- WriteSctpChunk(&sctpChunk);
- iChunkNum = iLenght = 0;
- }
- else
- {
-
- sctpChunkArr[iChunkNum++] = sctpChunk;
- iLenght +=sctpChunk.header.iLength;
- }
-
- iChunkOffset +=sctpChunk.header.iLength;
-
- if( iChunkOffset>=
- ipData.iTotalLength- ((ipData.byteHdLength & 0x0f) * 4))
- {
- if(iChunkNum > 0)
- {
-
- if(sctpChunk.header.Type != 0)
- iLenght-= sctpChunk.header.iLength;
-
- WritePkHeader(&data, iLenght + sctpChunk.header.iLength + LENGTH_SCTPALLHEADER(ipData));
- WriteEthInfo(eInfo);
- WriteIpHeader(&ipData, iLenght + sctpChunk.header.iLength + LENGTH_SCTPIPHEADER(ipData));
- WriteSctpHeader(&sctpHeader);
- for(i = 0; i < iChunkNum; i++)
- {
- WriteSctpChunk(sctpChunkArr + i);
- }
- }
- break;
- }
-
- }
- }
-
- free( pBuffer);
- }
-
-
- CloseOutFile();
- ClosePcapFile();
- printf( "Export over");
- return true;
- }
源码下载:http://download.csdn.net/detail/yhangleo/4998322
IP checksum 计算 :http://blog.csdn.net/yhangleo/article/details/8508003