IP数据包的校验和算法
1、算法思路:
IP/ICMP/IGMP/TCP/UDP等协议的校验和算法都是相同的,算法如下:
在发送数据时,为了计算IP数据包的校验和。应该按如下步骤: (1)把IP数据包的校验和字段置为0; (2)把首部看成以16位为单位的数字组成,依次进行二进制反码求和; (3)把得到的结果存入校验和字段中。 在接收数据时,计算数据包的校验和相对简单,按如下步骤: (1)把首部看成以16位为单位的数字组成,依次进行二进制反码求和,包括校验和字段; (2)检查计算出的校验和的结果是否等于零(反码应为16个0); (3)如果等于零,说明被整除,校验是和正确。否则,校验和就是错误的,协议栈要抛弃这个数据包。所谓的二进制反码求和,即为先进行二进制求和,然后对和取反。
计算对IP首部检验和的算法如下:
(1)把IP数据包的校验和字段置为0; (2)把首部看成以16位为单位的数字组成,依次进行二进制求和(注意:求和时应将最高位的进位保存,所以 加法应采用32位加法); (3)将上述加法过程中产生的进位(最高位的进位)加到低16位(采用32位加法时,即为将高16位与低16位 相加,之后还要把该次加法最高位产生的进位加到低16位) (4)将上述的和取反,即得到校验和。2、实现范例:
汇编:这个可以在linux内核代码里找到,下面就是 arch/x86/include/asm/checksum_32.h里面的内容:
/* * This is a version of ip_compute_csum() optimized for IP headers, * which always checksum on 4 octet boundaries. * * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by * Arnt Gulbrandsen. */ 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" /* 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 (__force __sum16)sum; }C语言:很多网络协议都用如下代码实现校验和算法:
/*求校验和函数*/ USHORT CheckSum(USHORT *buffer, int size) { unsigned long cksum=0; while (size > 1) { cksum += *buffer++; size -= sizeof(USHORT); } if (size) { cksum += *(UCHAR*)buffer; } /*对每个16bit进行二进制反码求和*/ cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)(~cksum); }