轉(zhuǎn)載自
https://blog.csdn.net/force_eagle/article/details/38546009
/*
Author: Godbach
本文可以自由轉(zhuǎn)載,但請(qǐng)標(biāo)明出處,并保證本文的完整性。
*/
對(duì)于數(shù)據(jù)包中檢驗(yàn)和的計(jì)算,很多講TCP/IP協(xié)議的書中都講過,RFC1071是討論計(jì)算檢驗(yàn)和的文檔。傳統(tǒng)的計(jì)算方法都是需要將整個(gè)數(shù)據(jù)報(bào)文的數(shù)據(jù)(IP報(bào)文是計(jì)算IP頭部的數(shù)據(jù))進(jìn)行反碼求和。
但在實(shí)際的應(yīng)用中,存在對(duì)收到的數(shù)據(jù)包進(jìn)行修改若干個(gè)地方,并回送至發(fā)送方或者轉(zhuǎn)發(fā)的情況。這個(gè)時(shí)候,就涉及的重新計(jì)算數(shù)據(jù)包的檢驗(yàn)和。最常見的可能就是,將收到的數(shù)據(jù)包的TTL字段減1,并轉(zhuǎn)發(fā)的情況。如果仍舊按照傳統(tǒng)的計(jì)算檢驗(yàn)和的方式進(jìn)行計(jì)算,特別是當(dāng)數(shù)據(jù)包長(zhǎng)度很大時(shí),為了重新計(jì)算校驗(yàn)和而將整個(gè)數(shù)據(jù)包的數(shù)據(jù)遍歷一邊,反碼求和,效率肯定比較低。
一、增量修改檢驗(yàn)和的實(shí)現(xiàn)
解決的方法就是采用增量式修改檢驗(yàn)和的方法。該方法是由RFC1141提出的。這里進(jìn)行以下簡(jiǎn)單的變通,將增量式修改檢驗(yàn)和的公式列出來:
HC -- 數(shù)據(jù)包中舊的檢驗(yàn)和
HC'-- 數(shù)據(jù)包中新的檢驗(yàn)和
m --數(shù)據(jù)包中某個(gè)域(16-bit字)修改前的值
m' -- 數(shù)據(jù)包中某個(gè)域(16-bit字)修改后的值
那么,修改某個(gè)域之后的校驗(yàn)和HC'與HC,m 和m'的關(guān)系如下:
HC' = HC + m + ~m' (公式1)
具體實(shí)現(xiàn)的C代碼如下:
/*implemented according with RFC 1071 and1141*/
static unsigned short csum_incremental_update(unsigned short old_csum,
unsigned short old_field,
unsigned short new_field)
{
unsigned long csum = old_csum + old_field + (~new_field & 0xFFFF);
csum = (csum >> 16) + (csum & 0xFFFF);
csum += (csum >> 16);
return csum;
}
這個(gè)時(shí)候,如果修改了IP頭部的TTL值,將取修改前的TTL域所在的16-bit字的值為old_field, 修改后的為new_field, 并取出就得檢驗(yàn)和old_csum,通過調(diào)用該函數(shù)即可計(jì)算出新的檢驗(yàn)和。
當(dāng)然該函數(shù)每次只能計(jì)算一個(gè)16-bit字,如果對(duì)數(shù)據(jù)包中修改了過多的16-bit字,那么它的性能將會(huì)如何呢。
用TCP數(shù)據(jù)包做測(cè)試。而且該TCP數(shù)據(jù)包為三次握手時(shí)的數(shù)據(jù)包,僅有頭部,沒有數(shù)據(jù)部分。對(duì)該數(shù)據(jù)包修改了8個(gè)不同的16-bit字,調(diào)用該函數(shù)8次所需的時(shí)間仍舊不到傳統(tǒng)計(jì)算方法的一半??梢?,效果適合明顯的。
二、增量修改檢驗(yàn)和的完善
-
RFC1071和1141所提出的增量式修改檢驗(yàn)有一個(gè)BUG,就是按照公式1計(jì)算新的檢驗(yàn)和時(shí),有可能出現(xiàn)計(jì)算結(jié)果為0xFFFF的情形,可以參看RFC1624給出的例子。如果檢驗(yàn)和為0xFFFF,則意味著數(shù)據(jù)包中所有部分相加的結(jié)果為0x0000。這是不可能的。因此,RFC1624提出了對(duì)這個(gè)BUG的改進(jìn)方法,見公式2:
HC' = (HC + ~m + m') (公式2)
公式2先對(duì)舊的檢驗(yàn)和和某個(gè)域的值取反,加上新的域值之后,將整體的和再次取反得出新的檢驗(yàn)和。
具體實(shí)現(xiàn)的C代碼如下:
/*implemented according with RFC 1624,modified the algorithm from RFC 1071 and 1141*/
static unsigned short csum_incremental_update_modified(unsigned short old_csum,
unsigned short old_field,
unsigned short new_field)
{
unsigned long csum = (~old_csum & 0xFFFF) + (~old_field &0xFFFF) + new_field ;
csum = (csum >> 16) + (csum & 0xFFFF);
csum += (csum >> 16);
return ~csum;
}
該方法和公式1給出的方法很明顯多出了兩次取反操作,在效率上并沒有公式1的高,這個(gè)也經(jīng)過了實(shí)際的測(cè)試。
-
對(duì)此,RFC1624又給出了另外一個(gè)方法既可以保證修正RFC1071和RFC1141的BUG,又可以保證執(zhí)行的效率,見公式3:
HC' = HC - ~m - m' (公式3)
公式3采用減法操作代替公示2中的加法操作。
具體實(shí)現(xiàn)的AT&T匯編代碼如下:
/*incremental update IP ,TCP, UDP checksum,
implemented in Assembly according with RFC1624, used subtraction to update checksum*/
static inline unsigned short csum_incremental_update(unsigned short old_csum,
unsigned shortold_field,
unsigned short new_field)
{
__asm__ __volatile__(
"notw %1; \n"
"subw %1, %0; \n"
"sbbw %2, %0; \n"
"sbbw $0, %0; \n"
:"=r" (old_csum)
:"r"(old_field), "r"(new_field),"0"(old_csum));
return old_csum;
}