pcap文件解析(三)--拆分SCTP包

這一章,我們將瞭解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

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章