IP資料包的校驗和演算法_儒雅

chief1985發表於2008-10-12
導讀:


在傳送資料時,為了計算數IP據報的校驗和。應該按如下步驟:
(1) 把IP資料包的校驗和欄位置為0。
(2) 把首部看成以16位為單位的數字組成,依次進行二進位制反碼求和
(3) 把得到的結果存入校驗和欄位中。

在接收資料時,計算資料包的校驗和相對簡單,按如下步驟:
(1)把首部看成以16位為單位的數字組成,依次進行二進位制反碼求和,包括校驗和欄位。
(2)檢查計算出的校驗和的結果是否等於零。
(3)如果等於零,說明被整除,校驗是和正確。否則,校驗和就是錯誤的,協議棧要拋棄這個資料包。


Linux 2.6核心中的校驗演算法,使用匯編語言編寫的,顯然效率要高些
/usr/src/linux-2.6.23/include/asm-i386/checksum.h

static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
{
    unsigned int sum;

    __asm__ __volatile__(
        "movl (%1), %0
-;/n"
        "subl $4,   %2
-;/n"
        "jbe 2f        ;/n"
        "addl 4(%1), %0 ;/n"
        "adcl 8(%1), %0 ;/n"
        "adcl 12(%1),%0 ;/n"
"1:     adcl 16(%1), %0 ;/n"
        "lea 4(%1), %1
-;/n"
        "decl %2        ;/n"
        "jne 1b       
-;/n"
        "adcl $0, %0    ;/n"
        "movl %0, %2    ;/n"
        "shrl $16, %0   ;/n"
        "addw %w2, %w0
-;/n"
        "adcl $0, %0    ;/n"
        "notl %0        ;/n"
"2:                     ;/n"
    : "=r" (sum), "=r" (iph), "=r" (ihl)
    : "1" (iph), "2" (ihl)
    : "memory");
    return (__force __sum16)sum;
}

(1) 將IP頭部(包括可選項)以32位為單位進行進位加法運算
(2) 將sum的低16位和高16位相加
(3) 取反

1b -- 1 before

在這個函式中,第一個引數顯然就是IP資料包的首地址,所有演算法幾乎一樣。需要注意的是第二個引數,它是直接使用IP資料包頭資訊中的首部長度欄位,不需要進行轉換,因此,速度又快了(高手就是考慮的周到)


第二種演算法就非常普通了,是用C語言編寫的。許多實現網路協議棧的程式碼,這個演算法是最常用的了,即使變化,也無非是先取反後取和之類的。考慮其原因,估計還是C語言的移植性更好吧。下面是該函式的實現:
unsigned short checksum(unsigned short *buf, int nword)
{
    unsigned long sum;

    for(sum = 0; nword > 0; nword--)
        sum += *buf++;

    sum = (sum>>16) + (sum&0xffff);
    sum += (sum>>16);

    return ~sum;
}



讓我們假設一個IP頭資料,來解cksum的惑

IP頭資料:

01000101                             /*ver_hlen*/
00000000                             /*tos*/
00000000 00000010                    /*len*/
00000000 00000000                    /*id*/
00000000 00000000                    /*offset*/
00000100                             /*ttl*/
00010001                             /*type*/
00000000 00000000                    /*cksum(0)*/
01111111 00000000 00000000 00000001
-/*sip*/
01111111 00000000 00000000 00000001
-/*dip*/

運算過程(注意是大端格式加):
for(sum = 0; nword > 0; nword--)
    sum += *buf++;

-01000101 00000000
-00000000 00000010
---------------------
-01000101 00000010
-00000000 00000000
---------------------
-01000101 00000010
-00000000 00000000
---------------------
-01000101 00000010
-00000100 00010001
---------------------
-01001001 00010011
-00000000 00000000
---------------------
-01001001 00010011
-01111111 00000000
---------------------
-11001000 00010011
-00000000 00000001
---------------------
-11001000 00010100
-01111111 00000000
---------------------
101000111 00010100
-00000000 00000001
---------------------
101000111 00010101            sum

                
sum = (sum>>16) + (sum&0xffff);
00000000 00000001            (sum>>16)
01000111 00010101            (sum&0xffff)
---------------------
01000111 00010110

sum += (sum>>16);
01000111 00010110
00000000 00000000            (sum>>16)
---------------------
01000111 00010110            sum

~sum
10111000 11101001            cksum  
      
說白了就是迴圈加,然後在取反!



對方機器呼叫checksum()計算校驗和,如果校驗和為0表明IP包傳輸正確
-----------------------------------------------------------
01000101                             /*ver_hlen*/
00000000                             /*tos*/
00000000 00000010                    /*len*/
00000000 00000000                    /*id*/
00000000 00000000                    /*offset*/
00000100                             /*ttl*/
00010001                             /*type*/
10111000 11101001                    /*cksum(0)*/
01111111 00000000 00000000 00000001 /*sip*/
01111111 00000000 00000000 00000001 /*dip*/

-01000101 00000000
-00000000 00000010
---------------------
-01000101 00000010
-00000000 00000000
---------------------
-01000101 00000010
-00000000 00000000
---------------------
-01000101 00000010
-00000100 00010001
---------------------
-01001001 00010011
-10111000 11101001
---------------------
100000001 11111100
-01111111 00000000
---------------------
110000000 11111100
-00000000 00000001
---------------------
110000000 11111101
-01111111 00000000
---------------------
111111111 11111101
-00000000 00000001
---------------------
111111111 11111110            sum

sum = (sum>>16) + (sum&0xffff);
00000000 00000001            (sum>>16)
11111111 11111110            (sum&0xffff)
----------------------
11111111 11111111

sum += (sum>>16);
11111111 11111111
00000000 00000000            (sum>>16)
----------------------
11111111 11111111

~sum
00000000 00000000


本文轉自
http://hi.baidu.com/zengzhaonong/blog/item/c933102403bc1530c89559fb.html

相關文章