就是把需要進行校驗的“字串”加( +)起來,把這相加的 結果取反當做
“ 校驗和” ( Checksum), 比如,相加的結果是 0101,那么“ 校驗和”就
是 1010,驗證的時候呢,就是 0101+1010 = 1111 ,取反后, 就是 0
先將checksum字段置零,然后按16位分組,計算2進制反碼和,最后再求和的反碼!
為了計算一份數據報的IP檢驗和,首先把檢驗和字段置為0。然后,對首部中每個16bit進行二進制反碼求和(整個首部看成是由一串16bit的字組成),結果存在檢驗和字段中。當收到一份IP數據報后,同樣對首部中每個16bit進行二進制反碼的求和。由於接收方在計算過程中包含了發送方存在首部中的檢驗和,因此,如果首部在傳輸過程中沒有發生任何差錯,那么接收方計算的結果應該為全1。如果結果不是全1(即檢驗和錯誤),那么IP就丟棄收到的數據報。但是不生成差錯報文,由上層去發現丟失的數據報並進行重傳。
當發送IP包時,需要計算IP報頭的校驗和:
1、 把校驗和字段置為0;
2、 對IP頭部中的每16bit進行二進制求和;
3、 如果和的高16bit不為0,則將和的高16bit和低16bit反復相加,直到和的高16bit為0,從而獲得一個16bit的值;
4、 將該16bit的值取反,存入校驗和字段。
◆當接收IP包時,需要對報頭進行確認,檢查IP頭是否有誤,算法同上2、3步,然后判斷取反 的結果是否為0,是則正確,否則有錯。
算法:
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); //將高16bit與低16bit相加 cksum += (cksum>>16); //將進位到高位的16bit與低16bit 再相加 return (USHORT)(~cksum); }
實例:
IP頭:
45 00 00 31
89 F5 00 00
6E 06 00 00(校驗字段)
DE B7 45 5D -> 222.183.69.93
C0 A8 00 DC -> 192.168.0.220
計算:
4500 + 0031 +89F5 + 0000 + 6e06+ 0000 + DEB7 + 455D + C0A8 + 00DC =3 22C4
0003 + 22C4 = 22C7
~22C7 = DD38 ->即為應填充的校驗和
當接受到IP數據包時,要檢查IP頭是否正確,則對IP頭進行檢驗,方法同上:
計算:
4500 + 0031 +89F5 + 0000 + 6E06+ DD38 + DEB7 + 455D + C0A8 + 00DC =3 FFFC
0003 + FFFC = FFFF
~FFFF = 00000 ->正確
TCP首部檢驗和與IP首部校驗和的計算方法相同,在程序中使用同一個函數來計算。
需要注意的是,由於TCP首部中不包含源地址與目標地址等信息,為了保證TCP校驗的有效性,在進行TCP校驗和的計算時,需要增加一個TCP偽首部的校驗和,定義如下:
struct { unsigned long saddr; //源地址 unsigned long daddr; //目的地址 char mbz;//置空 char ptcl; //協議類型 unsigned short tcpl; //TCP長度 }psd_header;
然后我們將這兩個字段復制到同一個緩沖區SendBuf中並計算TCP校驗和:
memcpy(SendBuf,&psd_header,sizeof(psd_header)); memcpy(SendBuf+sizeof(psd_header),&tcp_header,sizeof(tcp_header)); tcp_header.th_sum=checksum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_header));