IP/ICMP/IGMP/TCP/UDP等協議的校驗和算法都是相同的,算法如下:
在發送數據時,爲了計算IP數據包的校驗和。應該按如下步驟:
(1)把IP數據包的校驗和字段置爲0;
(2)把首部看成以16位爲單位的數字組成,依次進行二進制反碼求和;
(3)把得到的結果存入校驗和字段中。
在接收數據時,計算數據包的校驗和相對簡單,按如下步驟:
(1)把首部看成以16位爲單位的數字組成,依次進行二進制反碼求和,包括校驗和字段;
(2)檢查計算出的校驗和的結果是否等於零(反碼應爲16個1);
(3)如果等於零,說明被整除,校驗是和正確。否則,校驗和就是錯誤的,協議棧要拋棄這個數據包。
所謂的二進制反碼求和,即爲先進行二進制求和,然後對和取反。
IP數據報格式
假設IP頭爲:4500 0046 17d9 0000 4011 ec1d(校驗字段) ac1c 0f3b ac1c 0f3d
計算:
4500 + 0046 +17d9 + 0000 + 4011+ ec1d +ac1c + 0f3b + ac1c + 0f3d
取出的和相加再取反->即爲應填充的校驗和
當接受到IP數據包時,要檢查IP頭是否正確,則對IP頭進行檢驗,方法同上:
計算:
44500 + 0046 +17d9 + 0000 + 4011+ ec1d +ac1c + 0f3b + ac1c + 0f3d再與它們的和相加得出的一個數再次相加爲FFFF,得到的結果是全一,正確。
現假如一數據報爲45 00 05 D4 CA E0 40 00 75 06 70 D2 CA 62 39 64 C0 A8 00 02
根據IP數據報的格式可以看出它的首部校驗字段爲70 D2 它是怎麼算出來的呢?
方法:我們把首部校驗字段即70 D2 用0000代替
4500+05D4+CAE0+4000+7506+0000+CA62+3964+C0A8+0002=38F2A
然後把進出來的一位與後4位再進行十六進制加法,8F2A+0003=8F2D
最後用FFFF減去算出來的結果就可以了即FFFF-8F2D=70D2
代碼實現
SHORT checksum(USHORT* buffer, int size)
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if(size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum>>16) + (cksum&0xffff);
cksum += (cksum>>16);
return (USHORT)(~cksum);
}