1. IP 頭
https://www.cnblogs.com/xiehy/p/3166953.html
IP數據包也叫IP報文分組,傳輸在ISO網絡7層結構中的網絡層,它由IP報文頭和IP報文用戶數據組成,IP報文頭的長度一般在20到60個字節之間,而一個IP分組的最大長度則不能超過65535個字節。
下圖為IP分組的報文頭格式,報文頭的前20個字節是固定的,后面的可變。

| 版本:占4位(bit) |
指IP協議的版本號。目前的主要版本為IPV4,即第4版本號,也有一些教育網和科研機構在使用IPV6。在進行通信時,通信雙方的IP協議版本號必須一致,否則無法直接通信。 |
| 首部長度:占4位(bit) |
指IP報文頭的長度。最大的長度(即4個bit都為1時)為15個長度單位,每個長度單位為4字節(TCP/IP標准,DoubleWord),所以IP協議報文頭的最大長度為60個字節,最短為上圖所示的20個字節。 |
| 服務類型:占8位(bit) |
用來獲得更好的服務。其中的前3位表示報文的優先級,后面的幾位分別表示要求更低時延、更高的吞吐量、更高的可靠性、更低的路由代價等。對應位為1即有相應要求,為0則不要求。 |
| 總長度:16位(bit) |
指報文的總長度(包括報文頭)。注意這里的單位為字節,而不是4字節,所以一個IP報文的的最大長度為65535個字節。 |
| 標志(flag) |
該字段用於標記該報文是否為分片(有一些可能不需要分片,或不希望分片),后面是否還有分片(是否是最后一個分片)。 |
| 片偏移 |
指當前分片在原數據報(分片前的數據報)中相對於用戶數據字段的偏移量,即在原數據報中的相對位置。 |
| 生存時間:TTL(Time to Live) |
該字段表明當前報文還能生存多久。每經過1ms或者一個網關,TTL的值自動減1,當生存時間為0時,報文將被認為目的主機不可到達而丟棄。使用過Ping命令的用戶應該有印象,在windows中輸入ping命令,在返回的結果中即有TTL的數值。 |
| 協議 |
該字段指出在上層(網絡7層結構或TCP/IP的傳輸層)使用的協議,可能的協議有UDP、TCP、ICMP、IGMP、IGP等。 |
| 首部校驗和 |
用於檢驗IP報文頭部在傳播的過程中是否出錯,主要校驗報文頭中是否有某一個或幾個bit被污染或修改了。 |
| 源IP地址:32位(bit) |
4個字節,每一個字節為0~255之間的整數,及我們日常見到的IP地址格式。 |
| 目的IP地址:32位(bit) |
4個字節,每一個字節為0~255之間的整數,及我們日常見到的IP地址格式。 |
/*IP頭定義,共20個字節*/
typedef struct _IP_HEADER
{
char m_cVersionAndHeaderLen; //版本信息(前4位),頭長度(后4位)
char m_cTypeOfService; // 服務類型8位
short m_sTotalLenOfPacket; //數據包長度
short m_sPacketID; //數據包標識
short m_sSliceinfo; //分片使用
char m_cTTL; //存活時間
char m_cTypeOfProtocol; //協議類型
short m_sCheckSum; //校驗和
unsigned int m_uiSourIp; //源ip
unsigned int m_uiDestIp; //目的ip
} __attribute__((packed))IP_HEADER, *PIP_HEADER ;
2. IP校驗和
https://www.cnblogs.com/yd1227/archive/2011/06/30/2094309.html
當發送IP包時,需要計算IP報頭的校驗和:
1、 把校驗和字段置為0;
2、 對IP頭部中的每16bit進行二進制求和;
3、 如果和的高16bit不為0,則將和的高16bit和低16bit反復相加,直到和的高16bit為0,從而獲得一個16bit的值;
4、 將該16bit的值取反,存入校驗和字段。
3. TCP head
https://www.cnblogs.com/li-hao/archive/2011/12/07/2279912.html

源端口: 長度為16位,2個字節。
目的端口: 長度為16位,2個字節。
IP實現了點到點的數據通信,而TCP實現的是端到端的通信。
通信端用一個IP與端口號來唯一標識。(其實端口號就是用來標識同一主機中的不同進程。)
IP協議負責將數據傳輸到目標主機,而TCP可以根據數據報中的端口號,將數據交給相應的程序進行處理。
序列號: 長度32位,4個字節。
確認序列號:長度32位,4個字節。
頭部長度:該字段占用4位,用來表示報文首部的長度,單位是4Byte。如:headLen = ((packet[12]>>4)&0x0F)*4;
預留6位:長度為6位,作為保留字段,暫時沒有什么用處。
URG:長1位,表示緊急指針字段有效;
ACK:長1位,置位表示確認號字段有效;
PSH:長1位,表示當前報文需要請求推(push)操作;
RST:長1位,置位表示復位TCP連接;
SYN:長1位,用於建立TCP連接時同步序號;
FIN:長1位,用於釋放TCP連接時標識發送方比特流結束;
窗口大小:長度為16位,2個字節。
校驗和:長度為16位,2個字節。
緊急指針:長度為16位,2個字節。
以上是TCP包頭必須要有的字段,也稱固有字段,長度為20個字節。
可選項:此項是可選項(可有可無),解包時得具體分析(是TCP包頭的一部分)。
/*TCP頭定義,共20個字節*/
typedef struct _TCP_HEADER
{
short m_sSourPort; // 源端口號16bit
short m_sDestPort; // 目的端口號16bit
unsigned int m_uiSequNum; // 序列號32bit
unsigned int m_uiAcknowledgeNum; // 確認號32bit
short m_sHeaderLenAndFlag; // 前4位:TCP頭長度;中6位:保留;后6位:標志位
short m_sWindowSize; // 窗口大小16bit
short m_sCheckSum; // 檢驗和16bit
short m_surgentPointer; // 緊急數據偏移量16bit
}__attribute__((packed))TCP_HEADER, *PTCP_HEADER;
/*TCP頭中的選項定義
kind(8bit)+Length(8bit,整個選項的長度,包含前兩部分)+內容(如果有的話)
KIND = 1表示 無操作NOP,無后面的部分
2表示 maximum segment 后面的LENGTH就是maximum segment選項的長度(以byte為單位,1+1+內容部分長度)
3表示 windows scale 后面的LENGTH就是 windows scale選項的長度(以byte為單位,1+1+內容部分長度)
4表示 SACK permitted LENGTH為2,沒有內容部分
5表示這是一個SACK包 LENGTH為2,沒有內容部分
8表示時間戳,LENGTH為10,含8個字節的時間戳
*/
typedef struct _TCP_OPTIONS
{
char m_ckind;
char m_cLength;
char m_cContext[32];
}__attribute__((packed))TCP_OPTIONS, *PTCP_OPTIONS;
4. TCP校驗和
TCP頭前加一個偽首部,其它步驟和IP類似。

| unsigned short CheckSum(unsigned short *_pBuff, int _Size) { unsigned int ckSum = 0;
unsigned short *tmpBuff = _pBuff; int tmpSize = _Size;
while (tmpSize > 1) { ckSum += *tmpBuff ++; tmpSize -= sizeof(unsigned short); }
if (tmpSize > 0) { ckSum += *(unsigned char*)tmpBuff; }
ckSum = (ckSum >> 16) + (ckSum & 0xFFFF); //將高16bit與低16bit相加 ckSum += (ckSum >> 16); //將進位到高位的16bit與低16bit 再相加
return (unsigned short)(~ckSum); } |
5. 檢驗和更新
https://blog.csdn.net/qq_43395215/article/details/103414009
如果在報文的轉發路徑中遇到需要修改ip、tcp中部分報文字段,那么校驗和需要重新計算。
當修改只是一小部分時,可以用下列方法更新校驗和。
IP頭校驗和、TCP/UDP校驗和更新
| unsigned short updateCheckSum(unsigned short wOldCheckSum, unsigned short wOld, unsigned short wNew) { unsigned int dwCheckSum = wOldCheckSum + wOld + (~wNew & 0xFFFF); dwCheckSum = (dwCheckSum >> 16) + (dwCheckSum & 0xFFFF); dwCheckSum += (dwCheckSum >> 16); return (unsigned short)dwCheckSum; }
unsigned short updateCheckSum32(unsigned short wOldCheckSum, unsigned int dwOld, unsigned int dwNew) { return UpdateCheckSum(UpdateCheckSum(wOldCheckSum, (unsigned short) (dwOld >> 16), (unsigned short) (dwNew >> 16)), (unsigned short) dwOld, (unsigned short) dwNew); } |
