解析IPV4报文和IPV6报文的checksum的算法

解析IPV4报文和IPV6报文的checksum的算法:

校验和(checksum)算法,简单的说就是16位累加的反码运算:

计算函数如下:

我们在计算时是主机字节序,计算的结果封装成IP包时是网络字节序,注意这两者之间的区别,我们在从IP包里读取要转化为主机字节序,往IP包里存入时要转化为网络字节序在存入。

UINT32 Checksum(UINT32 cksum, VOID *pBuffer, UINT32 size)

{

INT8 num = 0;

UINT8 *p = (UINT8 *)pBuffer;



if ((NULL == pBuffer) || (0 == size))

{

    return cksum;

}



while (size > 1)

{

   cksum += ((UINT16)p[num] << 8 & 0xff00) | (UINT16)p[num + 1] & 0x00FF;

2个字节累加,先取网络字节序低位左移8位(变成主机字节序高位),与(加)上 网络字节序中的高位(主机字节序地位),即网络字节序要先变成主机字节序在进行累加,

    size  -= 2;

    num   += 2;

}

if (size > 0)

如果长度为奇数

{

    cksum += ((UINT16)p[num] << 8) & 0xFFFF;

如果总的字节数为奇数,则最后一个字节单独相加

    num += 1;

}



while (cksum >> 16)

{

    cksum = (cksum & 0xFFFF) + (cksum >> 16);

累加完毕将结果中高16位再加到低16位上,重复这一过程直到高16位为全0

}



return cksum;

}

注意:UINT32 cksum的类型,这里是4个字节的,防止在累加的过程中,数据溢出,(例如0xFF累加时就会内存溢出)

详细的计算过程和原理如下

一:

ip头的计算:

直接对头部数据进行累加(不包括原来的checksum值):

1、ipv4包头

   ipHeadLen  = (pIpHeader->ver_ihl & 0x0F) << 2;

在ipv4头中,版本类型和头长度加在一起是1个字节(8位),各占4位,版本类型在前,长度在后,所以要取长度只能取低4位,

   pIpHeader->chksum = 0;

因为不包括原来的checksum值,所以在每次计算前先把checksum的值置0,然后计算

   sum = Checksum(0, (VOID *)pIpHeader, ipHeadLen);

对整个ip包头的累加

   pIpHeader->chksum = HTONS((UINT16)(~sum));

结果为计算值的反码,(别忘转化为网络字节序)

2、ipv6包头

在ipv6中已经省略了checksum部分,但在后面的部分要有的,比如TCP/UDP包,别高兴的太早

二、

TCP/UDP报文的计算(举例UDP):

这里的checksum包含两部分,一部分是伪头的累加,还有一部分是UDP包的累加(不包括原来的checksum值)

伪头有分ipv4和ipv6两种,分别包含如下几部分,这里做下比较

IPV4

IPV6

目的地址

4字节(32位)

16字节(128位)

源地址

4字节(32位)

16字节(128位)

协议类型

1字节(8位)(Protocol)

1字节(8位)(next header)

(TCP/UDP)长度

2字节(16位)

2字节(16位)

1、 ipv4类型的:

第一部分,伪头部分的计算:

sum = 0;

    udpLen  = sizeof(UDP_HEADER_T) + dhcpLen;

UDP的长度= UDP的包头长度+ UDP的数据长度

sum += udpLen;

或者,下面也是一样的,这里就是网络字节序和主机字节序的区别了,上面的是(主机字节序)直接累加,下面的是网络字节序,一定要变成主机字节序后累加

   pUdpHeader->len = HTONS(udpLen);

主机字节序转化为网络字节序,存入数据包中,一定要注意,我们做的所有累加也是网络字节序,这里一定要搞清楚,以防混淆搞错了

   sum += (pUdpHeader->len >> 8 & 0x00FF);

2个字节的累加,先取网络字节序的高位,右移8位,变成主机字节序的低位,累加

   sum += (pUdpHeader->len << 8 & 0xFF00);

在取网络字节序的低位,左移8位,变成主机字节序的高位,累加

   sum  = Checksum(sum, (VOID *)&pIpHeader->saddr, 4);

    sum  = Checksum(sum, (VOID *)&pIpHeader->daddr, 4);

对4位的地址进行累加

   sum += ((UINT16)pIpHeader->proto & 0x00FF);

对1位的协议类型进行累加

伪头部分计算完成

第二部分,UDP数据包的计算

         pUdpHeader->chksum = 0;

注意:每次计算前别忘先把checksum的值置0,然后计算

   sum = Checksum(sum, (VOID *)pUdpHeader, udpLen);

对整个UDP包的累加

   pUdpHeader->chksum = HTONS((UINT16)(~sum));

结果为计算值的反码,(别忘转化为网络字节序)

UDP数据包部分计算完成

2、 ipv6类型的:

第一部分,伪头部分的计算:

sum = 0;

    udpLen  = sizeof(UDP_HEADER_T) + dhcpLen;

sum += udpLen;

或者

   pUdpHeader->len = HTONS(udpLen);

    sum += (pUdpHeader->len >> 8 & 0x00FF);

    sum += (pUdpHeader->len << 8 & 0xFF00);



    sum  = Checksum(sum, (VOID *)&pIpHeader->saddr, 16);

    sum  = Checksum(sum, (VOID *)&pIpHeader->daddr, 16);

对16位的地址进行累加

   sum += ((UINT16)pIpHeader->proto & 0x00FF);             

伪头部分计算完成

第二部分,UDP数据包的计算

         pUdpHeader->chksum = 0;

注意:每次计算前别忘先把checksum的值置0,然后计算

   sum = Checksum(sum, (VOID *)pUdpHeader, udpLen);

对整个UDP包的累加

   pUdpHeader->chksum = HTONS((UINT16)(~sum));

结果为计算值的反码,(别忘转化为网络字节序)

UDP数据包部分计算完成

### IPv6 报文IPv4 报文的区别 #### 地址长度差异 IPv4 使用 32 位地址空间,而 IPv6 则采用 128 位地址空间[^1]。这意味着 IPv6 可以支持更多的设备连接到互联网。 #### 表示形式不同 IPv4 地址通常表示为四个十进制数的点分隔格式;相比之下,IPv6 地址则由八个十六进制数组成,每组之间用冒号分隔。例如,一个典型的 IPv6 地址看起来像这样 `2001:0db8:85a3:0000:0000:8a2e:0370:7334`[^4]。 #### 数据包头部结构变化 对于 IPv4 IPv6 的具体报文格式,在不参照特定图示的情况下可以描述如下: - **IPv4 头部字段** - 包含版本、首部长度、服务类型等基本参数。 - 存在一个选项字段用于携带额外的信息,这可能导致处理复杂度增加。 - **IPv6 头部简化设计** - 去除了部分冗余项如校验以及选项字段,使得路由器能够更快速地转发数据包。 - 新增了流标签字段来优化实时应用性能。 ```python # Python伪代码展示两种协议的数据包包头主要区别 class IPv4Header: version = None # 占4比特 ihl = None # 首部长度占4比特 dscp = None # 差异化服务代码点占6比特 ecn = None # 显式拥塞通知占2比特 total_length = None # 总长度占16比特 identification = None flags = None # 标志位占3比特 fragment_offset = None ttl = None # 生存时间占8比特 protocol = None # 上层协议类型占8比特 header_checksum = None source_address = None destination_address = None class IPv6Header: version = None # 占4比特 traffic_class = None# 流量类别占8比特 flow_label = None # 流标签占20比特 payload_length = None next_header = None # 下一头部占8比特代替protocol字段功能 hop_limit = None # 跳限取代TTL概念同样占8比特 source_address = None destination_address = None ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值