一、计算检验和的步骤
检验和的计算都是一个模板,只是各种检验和的初始数据不一样
总结一下就是:求和、回卷、取反
- 把校验和字段设置为0。
- 求和:把需要校验的数据看成以16位为单位的数字组成,依次进行二进制求和。
- 回卷:求和后超过16位的加到低16位。
- 取反:最后结果取反码就是检验和。
(1)可以每两个求和后回卷,再求和,在回卷,直到全部求和,然后取反
(2)也可以全部求和,然后一直回卷直到高 16 位全部为 0,然后取反
一般采用第二种
另外UDP、TCP数据报的长度可以为奇数字节,因为计算时是16位为单位,所以此时计算校验和时需要在最后一个填充字节0(只是计算校验和用,不发送出去)。
二、接收端校验校验和步骤
把需要校验的内容(包括校验和字段)看成以16位为单位的数字,依次进行二进制反码求和,如果结果是0表示正确,否则表示错误。
三、说明
校验和覆盖的内容:
- IP首部校验和:IP首部
- ICMP校验和:ICMP首部+ICMP数据
- UDP、TCP校验和:首部+数据+12个字节伪首部(源IP地址、目的IP地址、协议、TCP/UDP包长)
四、IP首部校验和
1、结构图如下
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191209142733172.png)
2、详细计算过程
为了不出问题,按照报文格式依次编写数据即可
版本和首部长度放在一起
- 版本:4
- 首部长度:20字节 ==》 5
- 总长度:46(首部 + 数据)
- 标识:
- 标志:是否分片,3 bit(不分片——010)
(1) 一个比特保留为以后用;
(2)第二个比特是DF(Don’t Fragment):“不分片”比特,若为1,IP将不对数据报进行分片,若无法将此数据报通过任何可用网络转发,则丢弃,并发送一个ICMP差错报文给起始端,若为0, 则在需要时将数据报分片;
(3)第三个比特是MF(More Fragment):“更多分片”比特,为1,表示后面还有更多的分片,为0,则表示是最后的分片。 - 偏移:偏移量,13 bit
- 生存时间:64
- 协议:17(UDP)
- 检验和:置0
- 源IP地址:192.168.1.81
- 目的IP地址:192.168.1.166
然后把这些数据全部二进制相加求和,回卷,取反。得到校验和:0xa443
不分片
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191211224023630.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191211223907142.png)
五、UDP校验和
1、结构图如下
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191209123708189.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjEwOTAxMg==,size_16,color_FFFFFF,t_70)
2、详细计算过程
为了不出问题,按照报文格式依次编写数据即可
- 源IP地址:192.168.1.81
- 目的IP地址:192.168.1.166
- 上层协议类型:17(UDP)
- UDP长度:26(首部 + 数据)
- 源端口:6081
- 目的端口:7081
- UDP长度:26(首部 + 数据)
- 检验和:置0
- 数据:81(18个,只是为了凑齐最小帧64字节)
然后把这些数据全部二进制相加求和,回卷,取反。得到校验和:0x6c2c
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019120914223582.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191209144314141.png)
六、代码及解释(代码一样,只是初始数据不一样)
1、检验和是16 bit 的
2、
- 数据有的是 8 位:所以两个一组,高位(在前面)左移8位(后面补0)变16 位,在后面 8 位加上低位,就组成了 16 bit
- 数据有的是 16 位:直接相加
3、因为求和会溢出,所以声明一个 32 bit 来存放和,回卷就直接把高 16 位,加到低 16 位,直到高 16 位就变成 0,然后再强转格式即可。
4、最后要声明的是数据的个数
#include <stdio.h>
#include <malloc.h>
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
uint8_t ipv4_8[] = {
0x45,
0x00,
0x00, 0x2e,
0x12, 0x34,
0x40, 0x00,
0x40,
0x11,
0x00, 0x00,
0xc0, 0xa8, 0x01, 0x51,
0xc0, 0xa8, 0x01, 0xa6,
};
uint8_t udp_8[] = {
0xc0, 0xa8, 0x01, 0x51,
0xc0, 0xa8, 0x01, 0xa6,
0x00, 0x11,
0x00, 0x1a,
0x17, 0xc1,
0x1b, 0xa9,
0x00, 0x1a,
0x00, 0x00,
0x51, 0x51, 0x51, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x51,
0x51, 0x51, 0x51, 0x51, 0x51, 0x51
};
uint16_t ipv4_16[] = {
0x4500, 0x002e,
0x1234, 0x4000,
0x4011, 0x0000,
0xc0a8, 0x0151,
0xc0a8, 0x01a6,
};
uint16_t udp_16[] = {
0xc0a8, 0x0151, 0xc0a8, 0x01a6,
0x0011, 0x001a, 0x17c1, 0x1ba9,
0x001a, 0x0000, 0x5151, 0x5151,
0x5151, 0x5151, 0x5151, 0x5151,
0x5151, 0x5151, 0x5151
};
uint16_t CheckSum_8(uint8_t array[], int len) {
uint32_t sum = 0;
int i;
for (i = 0; i < len - 1; i = i + 2) {
sum += ((uint16_t)array[i] << 8) + ((uint16_t)array[i + 1]);
}
if (i + 1 == len) {
sum += (uint16_t)array[i] << 8;
}
while (sum >> 16) {
sum = (sum >> 16) + (sum & 0xffff);
}
sum += (sum >> 16);
return (uint16_t)(~sum);
}
uint16_t CheckSum_16(uint16_t array[], int len) {
uint32_t sum = 0;
for (int i = 0; i < len - 1; i++) {
sum += array[i];
}
if (array[len - 1] >> 8) {
sum += array[len - 1];
}
else {
sum += array[len - 1] << 8;
}
while (sum >> 16) {
sum = (sum >> 16) + (sum & 0xffff);
}
sum += (sum >> 16);
return (uint16_t)(~sum);
}
int main() {
int udp_len_8 = sizeof(udp_8) / sizeof(udp_8[0]);
unsigned short check1 = CheckSum_8(udp_8, udp_len_8);
printf("udp_8 检验和为:%x\n", check1);
int udp_len_16 = sizeof(udp_16) / sizeof(udp_16[0]);
unsigned short check2 = CheckSum_16(udp_16, udp_len_16);
printf("udp_16 检验和为:%x\n", check2);
int ipv4_len_8 = sizeof(ipv4_8) / sizeof(ipv4_8[0]);
unsigned short check3 = CheckSum_8(ipv4_8, ipv4_len_8);
printf("ipv4_8 检验和为:%x\n", check3);
int ipv4_len_16 = sizeof(ipv4_16) / sizeof(ipv4_16[0]);
unsigned short check4 = CheckSum_16(ipv4_16, ipv4_len_16);
printf("ipv4_16检验和为:%x\n", check4);
return 0;
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019121122412698.png)
七、wireshark抓包查看
待验证
八、其它
原码、补码、反码
正整数部分:
(1)原码、反码和补码都一样
负整数部分:
(1)原码和反码的相互转换:符号位不变,数值位按位取反
(2)原码和补码的相互转换:符号位不变,数值位按位取反,末位再加1
九、参考
1、IP首部校验和结果
https://wenku.baidu.com/view/c5ad9131581b6bd97f19ea85.html
IP首部检验和:2f01
uint8_t data[] = {
0x45, 0x00, 0x00, 0x30,
0x4a, 0x3e, 0x40, 0x00,
0x80, 0x06, 0x00, 0x00,
0xc0, 0xa8, 0x00, 0x37,
0xc0, 0xa8, 0x00, 0x01
};
2、UDP校验和
https://blog.csdn.net/stone_yu/article/details/81611067
UDP检验和:285c
uint8_t data[] = {
0x0a,0xaa,0x3b,0xbf,
0xd2,0x0e,0x96,0x0d,
0x00,0x11,
0x00,0x1c,
0xd1,0x23,
0x27,0x42,
0x00,0x1c,
0x00,0x00,
0x6c,0x41,0x56,0x61,
0x00,0x00,0x0e,0x00,
0xf8,0xb6,0xd4,0x01,
0x93,0x13,0x00,0x00,
0x00,0x00,0x00,0x00
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)