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

  • 相关阅读:
    tomcat7:deploy (default-cli) on project myproject: Cannot invoke Tomcat manager: Software caused connection abort: socket write error
    android 更新版本案例
    Tomcat运行一段时间后,自动停止关闭,To prevent a memory leak,Druid 数据库连接自动关闭, the JDBC Driver has been forcibly unregistered.
    android调试debug快捷键
    android AlertDialog控件使用
    android RecyclerView的瀑布流布局案例
    android RecyclerView的Grid布局案例
    android RecyclerView的Linear布局案例
    android 子线程使用handle修改主线线程内容
    【CF840C】On the Bench DP
  • 原文地址:https://www.cnblogs.com/RodYang/p/3271832.html
Copyright © 2020-2023  润新知