解析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值):

1ipv4包头

 

       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));

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

 

2ipv6包头

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

 

二、

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

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

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

 

 

 

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数据包部分计算完成


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以太网帧格式: ![以太网帧格式](https://img-blog.csdn.net/20180605111112868) 1.前导码(Preamble):7个字节的0101...0101同步码,用于同步收发双方的时钟。 2.目的地址(Destination MAC Address):6个字节的目的MAC地址,表示数据帧的接收方。 3.源地址(Source MAC Address):6个字节的源MAC地址,表示数据帧的发送方。 4.类型(Type):2个字节,表示上层协议类型,如IP协议、ARP协议等。 5.数据(Data):46~1500个字节的数据部分,表示要传输的数据。 6.校验和(Frame Check Sequence, FCS):4个字节的校验和,用于检验数据的传输是否出错。 IPv4报文格式: ![IPv4报文格式](https://img-blog.csdn.net/20180605111708935) 1.版本(Version):4位,表示IP协议的版本号,IPv4的版本号为4。 2.首部长度(Header Length):4位,以4字节为单位的IPv4首部长度,最小值为5,最大值为15。 3.服务类型(Type of Service, TOS):8位,用于区分不同类型的服务,如低时延服务、高吞吐量服务等。 4.总长度(Total Length):16位,表示整个IPv4数据报的长度,包括首部和数据部分。 5.标识(Identification):16位,用于唯一标识一个IP数据报,由发送方生成。 6.标志(Flags):3位,分别为标志位、不分片位和更多片位。 7.片偏移(Fragment Offset):13位,表示该片段在原始IP数据报中的偏移量。 8.生存时间(Time to Live, TTL):8位,表示数据报在网络中可以传输的最大跳数。 9.协议(Protocol):8位,表示数据报的上层协议类型,如TCP协议、UDP协议等。 10.首部校验和(Header Checksum):16位,用于检验IP首部是否出错。 11.源地址(Source Address):32位,表示数据报的发送方IP地址。 12.目的地址(Destination Address):32位,表示数据报的接收方IP地址。 13.选项(Options):可选项,用于在IPv4首部中增加一些选项信息,如时间戳、记录路由等。 14.数据(Data):变长,表示要传输的数据。 IPv6分组格式: ![IPv6分组格式](https://img-blog.csdn.net/20180605112812143) 1.版本(Version):4位,表示IP协议的版本号,IPv6的版本号为6。 2.流量标签(Flow Label):20位,用于区分同一个流量的不同数据包。 3.有效载荷长度(Payload Length):16位,表示IPv6分组的有效载荷长度,包括扩展头和数据。 4.下一个头(Next Header):8位,表示IPv6分组中下一层协议的类型,如TCP协议、UDP协议等。 5.跳数限制(Hop Limit):8位,表示IPv6分组在网络中可以经过的最大路由跳数。 6.源地址(Source Address):128位,表示IPv6分组的发送方IP地址。 7.目的地址(Destination Address):128位,表示IPv6分组的接收方IP地址。 8.扩展头(Extension Headers):可选项,用于在IPv6分组中增加一些扩展的信息,如路由头、分段头等。 9.数据(Data):变长,表示要传输的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值