下面摘自《TCP/IP協議簇》的IP頭校正和演算法:
發送時:
1. 將校正和欄位置為0;
2. 將整個首部分為16bit的部分,求和;
3. 取反碼,填入到校正和欄位中;
接收時:
1. 直接將整個首部分為16bit的部分,求和;
2. 取反碼,若結果為0,取合法;否則丟棄;
這上面有兩個細節沒有描述清楚:
1. 計算時的位元組順序(litter endian和big endian)問題;
2. 取和溢出時的改進計算方法;
根據實驗結果,及參考網路上的資料,實際上幾乎現有所有的系統對校正和演算法已經有點小小的補充,也許《TCP/IP協議簇》這裡沒有更新罷了,自我安慰吧,如下:
◆當發送IP包時,需要計算IP前序的校正和:
1、 把校正和欄位置為0;
2、 對IP頭部中的每16bit進行二進位求和;
3、 如果和的高16bit不為0,則將和的高16bit和低16bit反覆相加,直到和的高16bit為0,從而獲得一個16bit的值;
4、 將該16bit的值取反,存入校正和欄位。
◆當接收IP包時,需要對前序進行確認,檢查IP頭是否有誤,演算法同上2、3步,然後判斷取反的結果是否為0,是則正確,否則有錯。
演算法:
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);
}
關於計算時的位元組順序,一般以網路位元組順序(big endian)為準,但仍然有個十分模糊的地方,就是為什麼校正和這個欄位不用進行網路轉換了,如X86系統,直接以本地位元組順序(litter endian)發送就可以了,這也讓我十分糾結。
下面是舉例:
char szBuf[] = {'\x45', '\x00', '\x00', '\xf4', '\x00', '\x2e', '\x00', '\x00' ,'\x80' ,'\x11',
'\x00', '\x00', '\xc0', '\xa8', '\x09', '\x0a', '\xc0', '\xa8', '\x09', '\xff'};
unsigned short SendCkSum = 0x71a5; //這個值不用轉換為網路位元組順序,直接以 A5 71填入
char szBuf[] = {'\x45', '\x00', '\x00', '\xf4', '\x00', '\x2e', '\x00', '\x00' ,'\x80' ,'\x11',
'\xa5', '\x71', '\xc0', '\xa8', '\x09', '\x0a', '\xc0', '\xa8', '\x09', '\xff'};
unsigned short RecvCkSum = CheckSum((unsigned short*)szBuf, 20); //這裡RecvCkSum為0