增量式修改檢驗(yàn)和(IP, TCP, UDP)算法的研究和實(shí)現(xiàn)

轉(zhuǎn)載自

https://blog.csdn.net/force_eagle/article/details/38546009

/*

  • Author: Godbach

  • E-mail:nylzhaowei@163.com

  • 本文可以自由轉(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)和的完善

  1. 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è)試。

  1. 對(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;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容