這一章,我們將瞭解SCTP數據包結構,並簡要介紹SCTP協議,最後將帶有多個chunk的SCTP包拆分問單個SCTP數據包。
SCTP數據包
數據包頭 |
Eth信息 |
IP頭 |
SCTP頭 |
SCTP Chunk 1 |
…… |
SCTP Chunk n |
其中數據包頭和IP頭已經在前面做過介紹了,這裏先簡單介紹一下Eth信息。
// Ethernet 信息
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結構體定義
// SCTP頭
typedef struct __Sctp_header
{
_Int16 SrcPort;
_Int16 DstPort;
_Int32 iVerTag;
_Int32 iChecksum;
} __SctpHeader;
// chunk頭
typedef struct __Sctp_chunk_header
{
Byte Type;
Byte Flag;
_Int16 iLength;
} __SctpChunkHeader;
// 單個SCTP Chunk
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);
// 獲得ETH信息
GetEthernetInfo( ðInfo, pBuffer,0);
// 獲得IP信息
GetIpData( &ipData, pBuffer,sizeof(__EthernetInfo));
// SCTP == 132
if( ipData.byteProtocol == 132)
{
// 獲取SCTP頭
int iOffset = sizeof(__EthernetInfo) + ipData.byteHdLength * 4;
int iChunkOffset =iOffset + sizeof( __SctpHeader);
__SctpHeader sctpHeader;
__SctpChunksctpChunkArr[MAX_CHUNK_NUM];
// 當前已保存的chunk數量
int iChunkNum = 0;
// 當前已保存的chunk長度
int iLenght = 0;
// 獲得SCTP頭
GetSctpHeader(&sctpHeader, pBuffer, iOffset);
while( true)
{
// 當前讀取的chunk
__SctpChunksctpChunk;
// for 循環標誌
int i = 0;
GetSctpChunk(&sctpChunk, pBuffer, iChunkOffset);
if(sctpChunk.header.Type == 0) // DATA塊 建立新數據包並寫入chunk信息
{
WritePkHeader(&data, iLenght + sctpChunk.header.iLength + LENGTH_SCTPALLHEADER(ipData));
WriteEthInfo(ðInfo);
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
{
// 當前塊位非DATA塊
sctpChunkArr[iChunkNum++] = sctpChunk;
iLenght +=sctpChunk.header.iLength;
}
iChunkOffset +=sctpChunk.header.iLength;
if( iChunkOffset>=
ipData.iTotalLength- ((ipData.byteHdLength & 0x0f) * 4))
{
if(iChunkNum > 0)
{
//存在未寫入的chunk數據,全部新建數據包寫入
if(sctpChunk.header.Type != 0)
iLenght-= sctpChunk.header.iLength;
WritePkHeader(&data, iLenght + sctpChunk.header.iLength + LENGTH_SCTPALLHEADER(ipData));
WriteEthInfo(ðInfo);
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