上海古都建筑设计集团,上海办公室装修设计公司,上海装修公司高质量的内容分享社区,上海装修公司我们不是内容生产者,我们只是上海办公室装修设计公司内容的搬运工平台

如何计算IP首部校验和Header CheckSum(计算详解、代码解析、例子)

guduadmin211月前

前言:本文灵感来自于上课的时候老师提出的问题。正是由于老师刨根问底地追问,才让我写下了这篇文章。

目录

什么是首部校验和?

首部校验和计算的过程

0.步骤

1. IP数据报格式

2. 例子

3.如何处理数据段不是单位的整数倍的情况

4.处理进位的情况

代码实现

ed1

ed2

代码解释

1. if(size) cksum += *(UCHAR*)buffer;

2. cksum = (cksum>>16) + (cksum&0xffff);

扩展

CRC

WireShark


什么是首部校验和?

首部校验和是在网络通信中常用的一种校验方法,用于验证数据包在传输过程中是否出现了错误或损坏。通常应用于网络层协议(如IP协议)的首部中。

首部校验和计算的过程

我先摘录一段话:

IP首部的检验和不采用复杂的CRC检验码而采用下面的简单计算方法:

在发送方,先把IP数据报首部划分为许多16位字的序列,并把检验和字段置零。用反码算术运算把所有16位字相加后,将得到的和的反码写入检验和字段。

接收方收到数据报后,将首部的所有16位字再使用反码算术运算相加一次。将得到的和取反码,即得出接收方检验和的计算结果。

若首部未发生任何变化,则此结果必为0,于是就保留这个数据报。否则即认为出差错。

看懂了吗?是不是很迷糊?

我来解释一下。

0.步骤

首部校验和的计算过程包括以下几个步骤:

  1. 将校验和字段的值初始化为0,作为校验和的累加器。
  2. 将首部中的每一段数据按照一定的单位(通常是16位)进行分组。
  3. 依次将这些数据段的值累加到校验和累加器中。
  4. 如果累加和的结果超过了该数据段所能表示的最大值,需要进行进位操作。
  5. 对累加和的最终结果进行取反,得到校验和的值。

还是很迷糊对不对?让我再来拆解一下。

1. IP数据报格式

首先,我们要了解IP数据报的格式。

如何计算IP首部校验和Header CheckSum(计算详解、代码解析、例子),第1张

如图,IP数据报以上部分组成,而计算首部校验和则需要用到以下12个字段。

没错,就是固定部分的所有内容(20字节)。

它包含:

  • 版本(Version)
  • 首部长度(Header Length)
  • 区分服务(Differentiated Service Field)
  • 总长度(Total Length)
  • 标识 (Identification)
  • 标志 (Flags)
  • 片偏移(Fragment Offset)
  • 生存时间 (Time to Live)
  • 协议(Protocol)
  • 首部检验和(Header CheckSum)
  • 源地址(Source Address)
  • 目的地址 (Destination Address)

    知道了这些东西,我们才能计算首部校验和。

    2. 例子

    用WireShark进行抓包,得到以下数据:

    如何计算IP首部校验和Header CheckSum(计算详解、代码解析、例子),第2张

    通过抓包工具/IP数据报的结构可知:

    (1)版本 = 4

    (2)首部长度 = 5

    (3)区分服务 =0(0x00)

    (4)总长度 = 60(0x003c)

    (5)标识 = 0973 (0x03cd)

    (6)标志 = 0

    (7)片偏移 = 0

    (8)生存时间 = 64(0x40)

    (9)协议 = 1

    (10)首部检验和 = 0xf38a

    (11)源地址 = 192.168.1.24(c0 a8 01 18)

    (12)目的地址 = 192.168.1.1(c0 a8 01 01)

    如何计算IP首部校验和Header CheckSum(计算详解、代码解析、例子),第3张

    我在此把二进制和十六进制都写了,计算的时候我按照16进制来。

    首部校验和=ffff-(4500+003c+03cd+0000+4001+c0a8+0118+c0a8+0101)=0xf38a

    与图片上显示出都首部校验和0xf38a一致。

    是不是很简单?

    非也,接下来要考虑到一些情况。

    3.如何处理数据段不是单位的整数倍的情况

    通常情况下,首部中的数据段是按照16位或32位进行划分的,而数据接收时可能会出现数据段不是单位的整数倍的情况。为了处理这种情况,我们可以采取以下方式:

    1. 首先,按照单位(如16位或32位)累加所有完整的数据段。
    2. 接下来,对于剩余不完整的数据段,将其视为一个字节的数据。
    3. 将这个字节数据累加到校验和累加器中。

    4.处理进位的情况

    在计算校验和的过程中,有可能会出现进位的情况。如果累加和的结果超过了对应数据段所能表示的最大值,就需要进行进位操作。

    处理进位的方法是:

    1. 将累加和右移一位,将进位的位数加到累加和中。
    2. 将累加和与进位的位数进行按位与操作,将进位带到低位,继续进行累加。
    3. 如果仍然有进位,重复以上步骤,直到没有进位为止。

    代码实现

    ed1

    USHORT checksum(USHORT* buffer, int size)
    {
        unsigned long cksum = 0;
        while(size>1)
        {
            cksum += *buffer++;/*
            这里实质上就是
            checkSum+=*pBuf;
            *pBuf++;
            */
            size -= sizeof(USHORT);
        }
        if(size)
        {
            cksum += *(UCHAR*)buffer;
        }
        /*对于接下来两行,可以写个通用的程序,这样如果产生了多于两次(三次及以上)进位时也能用,但是一般来说,进位不会超过2次
        while(ckSum>>16){
            cksum = (cksum>>16) + (cksum&0xffff); 
        }
        */
        cksum = (cksum>>16) + (cksum&0xffff); //这段代码是进行16位校验和的溢出处理
        cksum += (cksum>>16); 
        return (USHORT)(~cksum);
    }

    ed2

    上面代码的改版,思路来源于我的老师提出的问题:如果把指针的类型从USHORT改为UCHAR该怎么办?

    USHORT checksum(UCHAR* buffer, int size)
    {
        unsigned long cksum = 0;
        while(size > 1)
        {
            cksum += *(USHORT*)buffer;
            buffer += sizeof(USHORT);
            size -= sizeof(USHORT);
        }
        if(size)
        {
            cksum += *(UCHAR*)buffer;
        }
        while (cksum >> 16)
        {
            cksum = (cksum & 0xffff) + (cksum >> 16);
        }
        return (USHORT)(~cksum);
    }

    代码解释

    1. if(size) cksum += *(UCHAR*)buffer;

    这段代码的作用是处理IP首部大小不是2的整数倍时的剩余字节的情况。

    if(size)表示如果剩余的字节数不为0(即size不为0),则执行相应的代码。

    *(UCHAR*)buffer表示将指针buffer指向的地址强制转换为UCHAR类型的指针,也就是将指针buffer指向的地址视为一个字节的数据。

    checkSum += *(UCHAR*)buffer表示将这个强制转换后的UCHAR类型的字节数据加到校验和checkSum中。

    为什么不是*(UCHAR*)checkSum呢?因为checkSum此时是用来累加校验和的变量,并不代表具体的数据地址。实际上,checkSum存储的是累加后的校验和结果。

    而这段代码的目的是处理IP首部中剩余的不足2个字节的情况,因此需要将buffer指向的具体地址(代表剩余的字节)当作一个字节的数据加入校验和中。

    总结起来,代码*(UCHAR*)buffer的作用是将buffer指向的地址视为一个字节的数据,并将其加入到校验和checkSum中以进行处理。

    为什么要强制类型转换?因为不做类型转换会加到不该加的东西,会多加一个字节的内容。

    2. cksum = (cksum>>16) + (cksum&0xffff);

    checkSum是一个无符号长整型,有可能会存在大于16位的计算,要进行高位溢出的处理。这时,要将checkSum右移16位,把高16位与低16位分开计算,并将结果相加。其中, >>表示右移操作,表示将checkSum向右移动16位。

    (checkSum&0xffff)是为了保留checkSum的低16位,&运算符是位运算符,并且是按位与,(&&是逻辑与),将checkSum中的低16位与0xffff进行按位与操作,即得到checkSum的低16位。

    一旦完成高16位与低16位的相加,如果还有进位,需要将进位的位数再加到checkSum中,以确保校验和的正确性。在这里可以用while循环来写。(如代码)

    因此,这段代码的作用是为了保证IP首部校验和的正确性,处理了16位校验和的溢出,使用了位运算符>>和&,即右移和按位与。

    扩展

    CRC

    CRC(Cyclic Redundancy Check)循环冗余校验,是一种数据传输检错技术。它通过在数据帧中添加校验位来检测数据传输过程中出现的差错。CRC校验算法是一种基于二进制多项式除法的校验方法,它可以检测出多种数据传输错误,如单比特差错、双比特差错、突发错误等。在计算机网络、通信、存储等领域中广泛应用。

    WireShark

    WireShark是一款流行的网络封包分析工具,可以截取各种网络数据包,并显示数据包详细信息。它可以用于网络故障排查、网络安全分析、网络协议开发等方面。WireShark提供了丰富的过滤器功能,可以根据协议、端口、主机名、数据包内容等多种条件进行过滤,方便用户快速定位需要分析的数据包。同时,WireShark还提供了多种统计和图形化分析工具,可以帮助用户更好地理解网络数据流量和协议行为。如果你需要进行网络数据包分析,WireShark是一个非常好的选择。

网友评论

搜索
最新文章
热门文章
热门标签
 
 梦见和已故的长辈说话  周公解梦原文古版翻译  周公解梦大全查询梦见蛇是啥预兆