以前在學謝希仁教授主編的《計算機網絡》時,一直沒弄懂IP首部的校驗和是如何計算的。今天在看《TCP/IP詳解 卷一》時,看到了一段關於首部校驗和的描述。如下:
爲了計算一份數據報的IP校驗和,首先把檢驗和的字段設置爲0。然後,對首部中每個16bit進行二進制反碼求和(整個首部看成是由一串16bit的字組成),結果存在校驗和字段中。當收到一份IP數據報後,同樣對首部中每16bit進行二進制反碼求和。由於接收方在計算過程中包含了發送方存在首部中的檢驗和,因此,如果首部在傳輸過程中沒有發生任何差錯,那麼接收方計算的結果應該全爲1。如果結果不是全1,即檢驗和錯誤。
通過Wireshark抓到一個IP數據包如下,
也就是說IP數據包未發送時的頭部爲:
head = [0x45c0, 0x0028,
0x4d1c, 0x4000,
0x2c06, 0x0000,
0x3a9a, 0xc703,
0xc0a8, 0x8226]
對應的校驗和計算如下(Python腳本):
def checksum(head):
sum = 0
for i in range(0, len(head)):
sum = sum + (0xffff-head[i])
#print "%4X"%sum
sum = (sum>>16)+(sum & 0xffff) # why ?
#print "%4X"%(sum)
return sum
爲什麼需要sum = (sum>>16)+(sum & 0xffff)這一步?
答:這是二進制反碼求和算法決定的。即計算校驗和時,若相加後最高位有進位,那麼不能捨棄,一定要加到低位,才能是結果正確。
現在,IP數據包的頭部爲:
head = [0x45c0, 0x0028,
0x4d1c, 0x4000,
0x2c06, 0xBC87,
0x3a9a, 0xc703,
0xc0a8, 0x8226]
再調用checksum方法一次,將得到 全1 的最終結果。