对UDP校验和的理解_udp 数据包 校验和 checksum=0-程序员宅基地

技术标签:   

很多文章对ip首部检验和的计算介绍得很简略,在理解上常常会比较困难。这篇文章是我自己的一些理解。或许也有不正确的地方,希望大家指正。

这个问题一直困绕了我很长时间,今天终于理解了。


我们可以通过spynet sniffer抓包软件,抓取一个ip数据包进行分析研究。
下面我以本机抓到的一个完整的ip首部为例(红色字体表示):

0000: 00 e0 0f 7d 1e ba 00 13 8f 54 3b 70 08 00 45 00
0010: 00 2e be 55 00 00 7a 11 51 ac de b7 7e e3 c0 a8
0020: 12 7a

45 00 00 2e----4表示ip版本号为ip第4版;5表示首部长度为5个32 bit字长,即为20字节;00 2e表示ip总长度为46字节,其中ip数据部分为
26字节。
be 55 00 00----be 55表示标识符;00 00表示3 bit标志及13 bit片偏移量;
7a 11 51 ac----7a表示ttl值为122;11表示协议号为17的udp协议;51 ac表示16 bit首部检验和值;
de b7 7e e3----表示32 bit 源ip地址为222.183.126.227
c0 a8 12 7a----表示32 bit 目的ip地址为192.168.18.122




检验和计算:
首先,把检验和字段置为0。
45 00 00 2e
be 55 00 00
7a 11 00 00<----检验和置为0
de b7 7e e3
c0 a8 12 7a
其次,对整个首部中的每个16 bit进行二进制反码求和,求和值为3ae50,然后3+ae50=ae53(这是根据源代码中算法 cksum = (cksum
>> 16) + (cksum & 0xffff) 进行的 )

最后,ae53+51ac=ffff。因此判断ip首部在传输过程中没有发生任何差错。

"二进制反码求和" 等价于 "二进制求和再取反"
从源代码看,很关键的一点是二进制求出的和如果大于16位时所做的操作,用和值中高16位加上低16位的值作为最终的和值,然后再做取反运算.

 

The IP Header Checksum is computed on the header fields only.
Before starting the calculation, the checksum fields (octets 11 and 12)
are made equal to zero.

In the example code,
u16 buff[] is an array containing all octets in the header with octets 11 and 12

equal to zero.
u16 len_ip_header is the length (number of octets) of the header.


/*
**************************************************************************
Function: ip_sum_calc
Description: Calculate the 16 bit IP sum.
***************************************************************************
*/
typedef unsigned short u16;
typedef unsigned long u32;

u16 ip_sum_calc(u16 len_ip_header, u16 buff[])
{
 u16 word16;
 u32 sum=0;
 u16 i;
         
 // make 16 bit words out of every two adjacent 8 bit words in the packet
 // and add them up
 for (i=0;i<len_ip_header;i=i+2){
        word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF);
        sum = sum + (u32) word16;
 }


 // take only 16 bits out of the 32 bit sum and add up the carries
 while (sum>>16)
        sum = (sum & 0xFFFF)+(sum >> 16);

// one's complement the result
 sum = ~sum;

return ((u16) sum);
}

又一种写法

//计算校验和(直接相加,校验和需取反)

ip_buf->i.checksum=0;

ip_buf->i.checksum=~csum((WORD *)&ip_buf->i,sizeof(ipheader));

//ipheader是字节为单位的
WORD csum(void *dp, WORD count)
{
          register DWORD total=0;
          register WORD n, *p, carries;

          n = count / 2;
          p = (WORD *)dp;
          while (n--)
              total += *p++; //先加total = *p +total ;再P++;
          if (count & 1) //如果为单数,就是上面所count/2除不尽,
 {
 n=*(BYTE *)p;
              total +=n<<8;//n变成16位,在后面补0,再相加
 }
          while ((carries=(WORD)(total>>16))!=0)
              total = (total & 0xffff) + carries;
          return((WORD)total);
}

可以这样子理解:1110101,反码 0001010,两个相加的话,为1111111.

//

关于IP分组头的校验和(checksum)算法,简单的说就是16位累加的反码运算,但具体是如何实现的,许多资料不得其详。TCP和UDP数据报头也使用相同的校验算法,但参与运算的数据与IP分组头不一样。此外,IPv6对校验和的运算与IPv4又有些许不同。因此有必要对IP分组的校验和算法作全面的解析。

 

IPv4分组头的结构如下所示:

      0                      1                      2                      3      
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |Version|  IHL  |Type of Service|             Total Length            |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |            Identification           |Flags|         Fragment Offset       |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |  Time to Live |       Protocol      |           Header Checksum           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                          Source Address                             |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                       Destination Address                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                       Options                       |       Padding       |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

其中的"Header Checksum"域即为头校验和部分。当要计算IPv4分组头校验和时,发送方先将其置为全0,然后按16位逐一累加至IPv4分组头结束,累加和保存于一个32位的数值中。如果总的字节数为奇数,则最后一个字节单独相加。累加完毕将结果中高16位再加到低16位上,重复这一过程直到高16位为全0。下面用实际截获的IPv4分组来演示整个计算过程:

 

0x0000: 00 60 47 41 11 c9 00 09 6b 7a 5b 3b 08 00 45 00
0x0010: 00 1c 74 68 00 00 80 11 59 8f c0 a8 64 01 ab 46
0x0020: 9c e9 0f 3a 04 05 00 08 7f c5 00 00 00 00 00 00
0x0030: 00 00 00 00 00 00 00 00 00 00 00 00

 

在上面的16进制采样中,起始为Ethernet帧的开头。IPv4分组头从地址偏移量0x000e开始,第一个字节为0x45,最后一个字节为0xe9。根据以上的算法描述,我们可以作如下计算:

 

(1) 0x4500 + 0x001c + 0x7468 + 0x0000 + 0x8011 +
       0x0000 + 0xc0a8 + 0x6401 + 0xab46 + 0x9ce9 = 0x3a66d
(2) 0xa66d + 0x3 = 0xa670
(3) 0xffff - 0xa670 = 0x598f

 

注意在第一步我们用0x0000设置头校验和部分。可以看出这一分组头的校验和与收到的值完全一致。以上的过程仅用于发送方计算初始的校验和,实际中对于中间转发的路由器和最终接收方,可将收到的IPv4分组头校验和部分直接按同样算法相加,如果结果为0xffff,则校验正确。

 

如何编写产生IPv4头校验和的C程序?RFC1071(Computing the Internet Checksum)给出了一个参考实现 :

 

{

           /* Compute Internet Checksum for "count" bytes

            *         beginning at location "addr".

            */

       register long sum = 0;

 

        while( count > 1 )  {

           /*  This is the inner loop */

               sum += * (unsigned short) addr++;

               count -= 2;

       }

 

           /*  Add left-over byte, if any */

       if( count > 0 )

               sum += * (unsigned char *) addr;

 

           /*  Fold 32-bit sum to 16 bits */

       while (sum>>16)

           sum = (sum & 0xffff) + (sum >> 16);

 

       checksum = ~sum;

   }

 

对于TCP和UDP的数据报,其头部也包含16位的校验和,校验算法与IPv4分组头完全一致,但参与校验的数据不同。这时校验和不仅包含整个TCP/UDP数据报,还覆盖了一个虚头部。虚头部的定义如下:

 

                     0         7 8        15 16       23 24       31
                    +--------+--------+--------+--------+
                    |             source address              |
                       +--------+--------+--------+--------+
                    |           destination address           |
                    +--------+--------+--------+--------+
                    |  zero  |protocol| TCP/UDP length  |
                    +--------+--------+--------+--------+

 

其中有IP源地址,IP目的地址,协议号(TCP:6/UDP:17)及TCP或UDP数据报的总长度(头部+数据)。将虚头部加入校验的目的,是为了再次核对数据报是否到达正确的目的地,并防止IP欺骗攻击(spoofing)。

 

///

UDP校验方法:
UDP的CHECKSUM算法与[wiki]IP[/wiki]包的HEADER CHECKSUM的计算方法基本一样,只是取样数据不同。
UDP中,参与计算CHEKCSUM的数据包括三部分: 亚头部+UDP头部+数据部分
亚头部包括:2byte源IP地址+2byte目的IP地址+0x00+1byte[wiki]协议[/wiki]+2byte的UDP长度
UDP包头:2byte源端口+2byte目的端口+2byteUDP包长+0x0000(checksum)
数据部分

计算方法,以2字节为一个单位,将其顺序相加,就会产生2个字节的SUM,如果超过2字节,则将高位的值再加回低位,然后取补,得到的就是UDP的checksum

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weiweiliulu/article/details/17923043

智能推荐

VS2019中使用QT建立信号和槽函数连接(包括重载函数的情况)_vs2019 sln项目qt新建按钮的信号与槽函数-程序员宅基地

文章浏览阅读4.3k次。QT开发文档和网上提供了多种实现方法,参考https://doc.qt.io/qt-5/signalsandslots.html。这里分享个人认为比较好用和好理解的一种。1.在实现文件.c文件中用connect函数连接。使用方式:QObject::connect(信号发出者地址,信号发出者函数名称(信号发出者函数参数...),信号接收者地址,信号接收者槽函数名称(信号接收者槽函数参数...));注意:若在QObject子类内部定义信号槽,可直接调用connect函数。举个栗子:要实_vs2019 sln项目qt新建按钮的信号与槽函数

程序员可选择的个博客论坛网站_个人博客设计论坛官网-程序员宅基地

文章浏览阅读1.1k次。为想写技术博客的程序员推荐的个平台网站“对网上既有的一些帖子总结优化目录掘金CSDNGithubSegmentFault开源中国51CTO博客知乎专栏简书慕课网手记腾讯云社区阿里云社区博客网码云以上按个人好感度排序,展示方式为a.网站名称 网址b.logoc.百度简介d.主页界面1.掘金:https://juejin.im/timeline2.CSDN:https://blog.csdn.net/3.Github: https:/_个人博客设计论坛官网

【音视频开发好书推荐1】,androidstudio开发app教程-程序员宅基地

文章浏览阅读604次,点赞19次,收藏20次。RTC程序设计:实时音视频权威指南》系统地介绍了实时音视频系统的基础概念、工具、编程方法。《RTC程序设计:实时音视频权威指南》分为10章,第1章介绍信息与文字编码的基础知识,以及跨平台网络编程的基本方法。第2、3章介绍视频的采集与渲染技术。第4、5章介绍音频的采集与渲染技术。第6章介绍编解码相关的压缩技术。第7章介绍信令与媒体流的建连过程。第8章介绍网络传输过程中的优化技术。第9章介绍实时音视频的应用与可扩展架构。第10章介绍音视频系统核心指标及其计算方法。

【手把手】ios苹果打包——遇见项目实战|超详细的教程分享_如何编译 ios源码打包-程序员宅基地

文章浏览阅读1w次,点赞44次,收藏59次。六年代码两茫茫,不思量,自难忘6年资深前端主管一枚,只分享技术干货,项目实战经验关注博主不迷路~文章目录前言weex介绍eeui介绍一、安装CocoaPods1.CocoaPods介绍2.CocoaPods的安装二、登录开发者中心四、添加测试手机设备五、打包总结前言本教程手把手教你用weex+eeui框架打测试包ipa安装到手机(打正式包也有介绍)。eeui项目创建可看我的另一篇文章一文带你吃透eeui安卓开发weex介绍Weex是一个构建移动端跨平台UI框架。Weex使开发人员_如何编译 ios源码打包

Python 日期时间格式化输出,带年、月、日、时、分、秒_python日期格式化输出-程序员宅基地

文章浏览阅读2.9w次,点赞12次,收藏62次。Python 日期时间格式化输出,带年、月、日、时、分、秒输出中文日期格式:2022年01月03日 17时20分30秒**年**月**日**时**分**秒_python日期格式化输出

android app字体库,Android 自定义APP字体-程序员宅基地

文章浏览阅读449次。一、设置全局字体方法1:使用反射机制进行设置(1)把系统的typeface替换为自定义的Typeface typeFace = Typeface.createFromAsset(getAssets(), "fonts/aa.ttf");try {// xml属性值与Typeface属性值对应// normal Typeface.DEFAULT// sans Typeface..._android app 设置字体库

随便推点

简述计算机维修 维护的基本原则是什么,计算机硬件维护的四大原则-程序员宅基地

文章浏览阅读2.4k次。计算机硬件维护的四大原则近年来,随着社会经济的快速发展和计算机网络技术的广泛应用,如今计算机已经成为生产生活中不可或缺的必需品,随之而来就出现一些问题。下面是YJBYS小编为大家搜索整理的关于计算机硬件维护的四大原则,欢迎参考阅读,希望对大家有所帮助!想了解更多相关信息请持续关注我们应届毕业生培训网!对于计算机而言,主要包括硬件系统与软件系统两部分,其中硬件系统是软件系统功能得以实现的重要基础和前..._硬件维修的基本原则?

java有趣的技术分享ppt,java面试数据结构与算法高频考-程序员宅基地

文章浏览阅读736次。前言本文涵盖了阿里巴巴、腾讯、字节跳动、京东、华为等大厂的Java面试真题,不管你是要面试大厂还是普通的互联网公司,这些面试题对你肯定是有帮助的,毕竟大厂一定是行业的发展方向标杆,很多公司的面试官同样会研究大厂的面试题。与此同时,今年算法面试一定是会被问的,而算法不是光靠背面试题就有用的,它是需要数学逻辑思维的,因此,小编会在文末为大家准备一份非常优质的算法学习手册,重点在于学习思维方法,话不多说,直接开始上精选的大厂面试真题!在校生如果你是在校生,你应该趁着在学校的时间夯实基础(比如计算机系统、_java有趣的技术分享

MapReduce统计以某字母开头的单词的平均长度_mapreduce计算平均单词长度-程序员宅基地

文章浏览阅读1.7k次。MapReduce统计以某字母开头的单词的平均长度用MapReduce编写程序主要的就是编写Map和Reduce函数、main函数java代码如下package section1;import java.io.IOException;import java.util.StringTokenizer;import org.apache.hadoop.conf.Configuratio..._mapreduce计算平均单词长度

Java实现iOS内购实现记录_java ios内购-程序员宅基地

文章浏览阅读817次。支付凭证解析工具import javax.net.ssl.*;import java.io.BufferedOutputStream;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.net.URL;import java.security.cert.CertificateException;import java.security.cer_java ios内购

vagrant up报错 process_builder.rb:44:in `encode: “xE5“ to UTF-8 in conversion from ASCII-8BIT to UTF-_encode': "\\xe5" to utf-8 in conversion from ascii-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏2次。今天早上vagrant up 重装了都是报错了。报错信息:process_builder.rb:44:in `encode!': "\\xE5" to UTF-8 in conversion from ASCII-8BIT to UTF-8 to UTF-16LE (Encoding::UndefinedConversionError)解决方案:找到报错信息的process_builder.rb 44行,编码位置换成一下:#newstr.encode!('UTF-16LE')newstr_encode': "\\xe5" to utf-8 in conversion from ascii-8bit to utf-8 to gbk (enc

LED数码管段码_十六进制led段码数怎么求啊-程序员宅基地

文章浏览阅读4k次,点赞3次,收藏17次。LED数码管断码显示字符共阴极字形码共阳极字形码03FHC0H106HF9H25BHA4H34FHB0H466H99H56DH92H67DH82H707HF8H87FH80H96FH90HA77H88Hb7CH83HC39HC6HD5EHA1HE79H86HF71H8EHP73H8CHU3EHC1HT31_十六进制led段码数怎么求啊