引用鏈接:困或 知之可否
校驗和計算方法
1.哪些地方使用校驗和
- IP校驗和:IP首部。
- ICMP校驗和:ICMP首部+ICMP數據;
- UDP、TCP校驗和:首部+數據+12個字節偽首部(源IP地址、目的IP地址、協議、TCP/UDP包長)。
2.計算校驗和的步驟:
- 把校驗和字段設置為0。
- 把需要校驗的數據看成以16比特為單位的數字組成,依次進行二進制反碼求和。
- 把得到的結果存入校驗和字段中。
- 另外UDP、TCP數據報的長度可以為奇數字節,因為計算時是16比特為單位,所以此時計算校驗和時需要在最后增加一個填充字節0(只是計算校驗和用,不發送出去)。
3.接收端校驗校驗和步驟:
- 把需要校驗的內容(包括校驗和字段)看成以16比特為單位的數字,依次進行二進制反碼求和,如果結果是0表示正確,否則表示錯誤。
4.二進制反碼求和步驟:
- 二進制反碼求和,就是先把這兩個數取反,然后求和,如果最高位有進位,則向低位進1。
- 另外,先取反后相加與先相加后取反,得到的結果是一樣的。因此實現代碼都是先相加,最后再取反。
5.實現代碼:
static inline uint16_t check_sum(const uint16_t *buffer, int size){
//cksum 4個字節無符號整型
unsigned long cksum = 0;
//16位為單位數字相加
while(size>1){
cksum += *buffer++;
size -= sizeof(uint16_t);
}
//長度奇數情況
if(size){
cksum += *((unsigned char *)buffer);
}
//高位有進位,進位到低位,下面兩行代碼保證了高16位為0。
cksum = (cksum>>16) + (cksum&0xffff);
cksum += (cksum>>16);
//最后取反
return (uint16_t)(~cksum);
}
代碼解析
參數buffer是指向16位整數的指針,剛開始指向的是IP首部的起始地址,參數size是IP首部的大小。while循環是將IP首部的內容以16位為單元加在一起,如果沒有整除(即size還有余下的不足16位的部分),則加上余下的部分,此時的cksum就是相加后的結果,這個結果往往超出了16位,因為校驗和是16位的,所以要將高16位和計算得到的cksum再加工。
再加工第一步:cksum = (cksum>>16) + (cksum&0xffff); sum>>16是將高16位移位到低16位,sum&0xffff是取出低16位,相加得到新的cksum。
再加工第二步:cksum += (cksum>>16); 第一步相加時很可能會產生進位,因此要再次把進位移到低16位進行相加。
這樣就加工好了,接下來就是取反,並強制轉換為16位,這樣就得到了最終的校驗和。
校驗和計算出來了,接下來就是該如何校驗:
接收方進行校驗時,也是對每16位進行二進制反碼求和。接收方計算校驗和時的首部與發送方計算校驗和時的首部相比,多了一個發送方計算出來的校驗和。因此,如果首部在傳輸過程中沒有發生差錯,那么接收方計算的結果應該為全一,因為接收方計算除校驗和以外的部分得到值是校驗和的反碼,再加多出來的校驗和當然是全一了。