小端校验算法实验

/* 联合类型的变量类型,用于测试字节序
*  	成员value的高低端字节可以由成员type按字节访问
 */
typedef union{
	unsigned short int value;								/*短整型变量*/
	unsigned char byte[2];								/*字符类型*/
}to;

用上面这个联合来判断机器是大端还是小端

to t1,t2,s;
t1.value = 0x1234;
t2.value = 0x1234;
s.value = t1.value + t2.value;

 

已经知道自己的机器是小端的,一般x86的机器都是小端的,所以相加的过程如下
34 12
34 12
——
68 24

存储的顺序就是0x68 24,即就是s.value是0x24 68 s.byte[0]是68,s.byte[1]是24

那么用 t1 & 0xff00 结果应该是什么呢?

无论大端,小端结果都是1200
小端情况下:
t1:   34 12
&        00 ff
—————
00 12

 

大端机器情况下:

t1:12 34

&     ff 00

———————

12 00

虽然内部小端顺序是0012 和 大端顺序 1200
但是输出的结果都会是是 1200
这点是让我看了很久很晕的地方,这就是rfc1071 (校验和)上所说的Byte Order Independence,字节顺序的独立性

重点:所以我觉得RFC所描述的,最后一个奇数字节应该处理成为:0xAB00,无论大端还是小端,这个字节机器中的顺序都是这个
所以可以引出大端 需要左移8位,小端直接加的校验算法(值为00AB,在小端机器中显示为AB00)

 

读入网络字节顺序的数据流,无论你的机器是大端还是小端,通过checksum函数校验处理后,
输出结果值不一样,但是二进制流应该都是一样的,所以不需要再将结果转为网络字节序。(前提条件是数据是偶数个字节)

比如一个正确的校验结果是0xabcd,
当 小端=》值为 0xabcd,机器存 cd ab,赋值给checksum其实是赋值了机器顺序cd ab,这个不等于abcd,所以校验是错误的
那么小端需要的正确校验就是=》0xcd ab,赋值给checksum其实得到了abcd,校验正确。
大端=》值为 0xabcd 机器存ab cd,checksum也是abcd,校验是正确的
(tip:可以这么理解 机器只认二进制数据流,不认值的)

如果是奇数个字节,再看
大端机器,需要左移8位,再& 0xff00,就相当于构成了xx00这样的16位,再累加

小端机器,需不需要左移呢?(不需要)
t1值为0x1234
t1.value & htons(0xff00) 值: 34
机器内部:
34 12
ff 00
——
34 00
输出0034

t1.value & 0xff00 值:1200
34 12
00 ff
——-
00 12
输出1200

考虑这个输入:ABCDAB

在大端机器上:
如果是ABCD直接加AB,机器内部就是
ABCD
00AB
——–
这个结果不符合校验和,所以需要将AB左移8位,再累加。

在小端机器上:
如果是ABCD直接加AB,机器内部就是
CD AB
AB 00
——–

这个结果仍然不合符校验和,所以还是需要将AB左移8位,再累加
这样就是符合处理最后一个奇数字节的方式了,所以小端机器是不需要左移8位的
所以,无论大端小端机器,再加最后一个技术字节的时候,需要左移8位 & 0xff00,

—————————————-

现在做一个ping奇数包的实验,看看理论是否正确
机器:小端
ping奇数个包

/*校验函数*/
static unsigned short icmp_cksum(unsigned char *data,  int len)
{
       long sum=0;/* 计算结果 */
	int odd = len & 0x01;/*是否为奇数*/

	unsigned short *value = (unsigned short*)data;
	/*将数据按照2字节为单位累加起来*/
       while( len & 0xfffe)  {
              sum += *(unsigned short*)data;
		data += 2;
		len -=2;
       }
	/*判断是否为奇数个数据,若ICMP报头为奇数个字节,会剩下最后一字节。*/
       if( odd) {
		printf("befor odd sum:%x\n",sum);

		//unsigned short tmp = ((*data)<<8)&0xff00;
		unsigned short tmp = (*data);//主要就是上下这两句进行试验

		printf(" odd byte:%x\n",tmp);

              sum += tmp;

		printf("after odd sum:%x\n",sum);
       }

    while ( sum >> 16)
       sum = (sum >>16) + (sum & 0xffff);////可能存在进位,高16位的进位在加到低16位上

//       sum += (sum >>16) ;//上面操作可能又导致进位,在操作一次,不可能再出现进位情况

       
	printf("result:%x\nhtons:%x\n",(unsigned short)~sum,htons(~sum));
       return (unsigned short)~sum;
}

 

1、先是按照奇数位左移8位&0xff00,累加
icmp数据设置55字节(值都是0x01),8个字节icmp头

ex1

 

 

 

 

 

在没有加最后一个奇数字节时,值为1b2b
最后一个字节值为01,左移8位&0xff00 得 0x0100
再相加,最后结果为 0x1c2b
取反得到最后结果 0x e3d4
再看抓包情况

ex2

 

期望的校验码是0xd3e4,而我们的校验码是0xe3d4,也就是说 最后一节字节0x01加错了位置,
如果不左移8位&0xff00 ,那么推测校验一定是正确的。

修改代码后再ping 55个字节,可以看到已经收到返回的报文了

ex3

 

再看wireshark

ex4

 

我们result 的结果是 0xe4 d1,wireshark校验结果是正确的,给的值是 0x d1 e4
为什么result的值和wireshark的值,这两个是相同的?
因为wireshark显示的是从左到右的两个8 bits,d1 e4 ,这个 是网络字节序,
而我们的小端机器 结果是0x e4 d1,机器存储顺序就是d1 e4,赋值给check,校验时就是正确的。

至此,终于搞懂了网络校验和的原理了。

Tagged on: ,

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.