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

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

目录

什么是首部校验和?

首部校验和计算的过程

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数据报以上部分组成,而计算首部校验和则需要用到以下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数据报的结构可知:

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

我在此把二进制和十六进制都写了,计算的时候我按照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是一个非常好的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值