IP校验和主要是用来保证数据(IP包头)的完整性的.它用的算法很是简单,就是反码求和校验.须要注意的是反码求和又叫1的补码(one'scomplement),而2的补码就是咱们一般说的补码求和了.校验算法具体以下。html
将校验和字段置为0,而后将IP包头按16比特分红多个单元,如包头长度不是16比特的倍数,则用0比特填充到16比特的倍数;python
对各个单元采用反码加法运算(即高位溢出位会加到低位,一般的补码运算是直接丢掉溢出的高位),将获得的和的反码填入校验和字段;算法
发送数据包.网络
将IP包头按16比特分红多个单元,如包头长度不是16比特的倍数,则用0比特填充到16比特的倍数;oop
对各个单元采用反码加法运算,检查获得的和是否符合是全1(有的实现可能对获得的和会取反码,而后判断最终值是否是全0);spa
若是是全1则进行下步处理,不然意味着包已变化从而丢弃之.须要强调的是反码和是采用高位溢出加到低位的,如3比特的反码和运算:100b+101b=010b(由于100b+101b=1001b,高位溢出1,其应该加到低位,即001b+1b(高位溢出位)=010b),.net
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ unsigned short csum(unsigned char *addr, int count) { /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ register long sum = 0; while( count > 1 ) { /* This is the inner loop */ sum += * (unsigned short) addr++; count -= 2; } /* Add left-over byte, if any */ if( count > 0 ) sum += * (unsigned char *) addr; /* Fold 32-bit sum to 16 bits */ while (sum>>16) sum = (sum & 0xffff) + (sum >> 16); return ~sum; }
第一个while循环是作普通加法(2进制补码加法),由于IP包头和TCP整个报文段比较短(没达到2^17数量级),因此不可能致使4字节的sum溢出(unsigned long 通常至少为4字节)).unix
紧接着的一个判断语句是为了能处理输入数据是奇数个字节的这种状况.再接着的数据循环是实现反码算法(在前面的普通加法获得的数据的基础上),由反码和的高位溢出加到低位的性质,可获得"32位的数据的高位比特移位16比特,再加上原来的低16比特,不影响最终结果" 这个等价运算,由于sum的最初值(刚开始循环时)可能很大,因此这个等价运算需循环进行,直到sum的高比特(16比特以上)全为0.对于32 位的 sum,事实上这个运算循环至多只有两轮,因此也有程序直接用两条"sum = (sum & 0xffff) + (sum >> 16);"代替了整个循环.最后,对和取反返回.code
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ unsigned short cksum (struct ip *ip, int len) { long sum = 0; /* assume 32 bit long, 16 bit short */ while ( len >1 ) { sum += *((unsigned short *) ip)++; if (sum & 8x00000000) /* if high-order bit set, fold */ sum = (sum & 0xFFFF) + (sum>> 16) ; len -= 2; } if ( len ) /* take care of left over byte */ sum += ( unsigned short ) * (unsignedl char *) ip; while ( sum >> 16) sum =(sum & 0xFFFF) + (sum>> 16); return ~sum; }
这个实现与前面的一个的最大的不一样是对数据的长度没什么限制了,由于它在第一个循环的加法运算中实时检测sum的高位的值,一旦发现其有溢出的危险,就及时运用等价运算关系消除了这个危险.htm
Linux 2.6内核中的校验算法,使用汇编语言编写的,显然效率要高些。代码以下: unsigned short ip_fast_csum(unsigned char * 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" /* Since the input registers which are loaded with iph and ihl are modified, we must also specify them as outputs, or gcc will assume they contain their original values. */ : "=r" (sum), "=r" (iph), "=r" (ihl) : "1" (iph), "2" (ihl) : "memory"); return(sum); }
考虑这样的应用场景:路由器转发IP报文时,有可能只更改了IP数据包头的部份内容(如更改了TTL,分片了或SNAT更改了源IP等~~~),却须要重校验的问题.为提升转发效率,要求重校验算法尽量快,故出现了以下所示的重校验算法(只是一个简单的示例):
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ UpdateTTL(struct ip_hdr *ipptr, unsigned char n) { unsigned long sum; unsigned short old; old = ntohs(*(unsigned short *)&ipptr->ttl); ipptr->ttl -= n; sum = old + (~ntohs(*(unsigned short *)&ipptr->ttl) & 0xffff); sum += ntohs(ipptr->Checksum); sum = (sum & 0xffff) + (sum>>16); ipptr->Checksum = htons(sum + (sum>>16)); }
算法的实现依据是这样的.假设包头原校验和为~C,改变的字段的原始值是m,更改后的值是m',设~C'为重校验和,则有 ~C' = ~(C+(-m)+m') = ~C+(m-m') = ~C+m+~m'等价关系的成立基于反码的运算性质:取反运算知足结合律,按位取反运算与符号取反(及相反数)是等价的(即~C=-C).
若是有多个字段改变,只是上面的公式中的m和m'有多个而已,直接用反码加法搞定便可。
IP数据包校验要求速度快,因此只采用了简单的和校验,为何采用反码和而不是补码和呢?
反码和的溢出有后效性(蔓延性)
反码和将高位溢出加到低位,致使这个溢出会对后面操做有永久影响,有后效性;而补码和直接将高位和溢出,致使这个溢出对后面的操做再无影响,所以无后效性
反码校验无需考虑字节序
正由于反码和的溢出有后效性,致使大端字节序(big-endian)和小端字节序(little-endian)对同一数据序列(如两个16比特的序列)产生的校验和也只是字节序相反,而补码和由于将溢出丢掉了,不一样字节序之间的校验和大不相同且没什么联系。
基于以上的理由,校验和运算既可选择在数据被转换成网络字节序前,也可选择在以后,只要保证被校验的字段和填写的校验和字段的字节序保持一致就能够了。(这其实能够看做是负负得正,计算校验和与字节序有关,而后写校验和字段与字节序有关,而后直接计算校验和再写校验和字段则与字节序无关。)
http://blog.chinaunix.net/u/20/showart_438512.html,关于IP校验和的
http://blog.chinaunix.net/u/12313/showart_176114.html,关于网络校验和
http://www.wesoho.com/article/Delphi/2143.htm,关于IP校验和的
http://blog.chinaunix.net/u/20/showart_438418.html,关于补码和反码的