Skip to main content

Command Palette

Search for a command to run...

如何计算UDP头的checksum

Updated
1 min read
如何计算UDP头的checksum

UDP报头只有4个字段,分别是:源端口号、目的端口号、报文长度和报头checksum,其中的报头checksum这个字段在IPv4中并不是强制的,但在IPv6中是强制的,本文介绍UDP报头中checksum的计算方法,并给出相应的源程序,实际上,网络通信中常用的IP报头、TCP报头和UDP报头中都有checksum,其计算方法基本一样,所以把这些检查和一般统称为Internet Checksum;本文对网络编程的初学者难度不大。

1. UDP报文结构

  • UDP报文为两部分,报头+数据;
  • 在Linux下,UDP报头定义在头文件linux/udp.h中;

      struct udphdr {
          __be16    source;
          __be16    dest;
          __be16    len;
          __sum16    check;
      };
    
    • source - 来源端口号,可选项,如果不使用,填充 0;
    • dest - 目的端口号;
    • len - 报文长度;
    • check - 报头的校验和,在IPv4中是可选的,IPv6中是强制的,如果不使用,应填充0;

      UDP数据报结构

      图1:UDP数据报结构

2. IP报头结构

  • 之所以在这里介绍IP报头,是因为在计算UDP报头checksum时会用到IP头中的一些字段;
  • 在Linux下,IP报头定义在头文件linux/ip.h中,可以自行查看,我们这里仅给出图示;

    IP报头

    图2:IP报头结构

3. UDP报头checksum的计算

  • UDP报头checksum的定义及计算方法在RFC 768中有明确的说明;
  • UDP报头checksum的具体算法在RFC 1071有明确的说明;
  • 在计算UDP报头checksum前要首先为UDP报头加上一个伪报头;
  • 加上伪报头的UDP报文格式如下:

    伪报头

    图3:伪报头

  • 伪报头中源IP地址(Source IP address)、目的IP地址(Destination IP address)和协议(Protocol)这三个字段都是从IP报头中取过来的;
  • 源IP地址和目的IP地址是32-bit的IP地址,Protocol字段是网络协议号,UDP协议号为17(0X11);
  • 伪报头中 UDP length 这个字段指的是 UDP报头+数据 的长度,并不包括伪报头的长度,其值和UDP报头中的len字段应该是一样的;
  • 如果checksum中没有加入伪报头,UDP报文也是可以安全送达的,但是,如果IP报头有损坏或者被认为修改,报文有可能被送到错误的地址;
  • 伪报头中加入protocol字段是为了保证报文为UDP报文,正常情况下protocol为17(0x11),如果传输中这个字段变化了,那么在接收端计算出的checksum就会不正确,接收端会丢弃这个出现错误的报文;
  • checksum 计算规则:
    1. UDP报头中的check字段填充0;
    2. (伪报头+UDP报头+DATA)的长度应该为16-bit字的整数倍,如果不是,DATA的最后部分要填充1个字节0,以使其为16-bit字的整数倍;
    3. (伪报头+UDP报头+DATA)看作是很多个16-bit字,并逐一相加,结果仍为16-bit字,如果出现溢出,则结果+1,然后继续,直至完成;
    4. 结果求反即为所需的checksum;
  • 在RFC768中定义的UDP的checksum为:(伪报头+UDP报头+DATA)按16-bit字进行反码求和的结果就是checksum;但实际上原码求和再取反和反码求和是一样的结果,因为求反码再求和的方法对每一组16-bit字都要做一次求反运算,因此其运算量更大一些,在实际中没有人使用;
  • 以上两种运算方法在本文给出的范例中均有体现,可以用来验证其结果的一致性;
  • 按照RFC768的说明,当checksum的运算结果为0时,checksum应该作为全1来传输,因为在UDP协议中,checksum为0表示没有使用checksum,UDP的checksum在ipv4中并不是强制使用的。
  • 下面是计算udp报头checksum的完整源代码,源代码文件名:udp-checksum.c(点击文件名下载源程序)

  • 其中在计算checksum的程序中参考了RFC1071中给出的源代码;

  • checksum1()使用的常规的算法,checksum2()使用的把每个16-bit字求反再相加的算法进行的运算,两种算法的结果是一样的。
  • 读者可以根据需要适当地调整数据,以使其计算出不同的结果;
  • 编译:gcc -Wall udp-checksum.c -o udp-checksum
  • 运行:./udp-checksum
  • 下面是我的机器上的运行结果截屏

    程序运行截屏

    图4:程序运行截屏

4. UDP报头checksum的验证

  • UDP报文的接收端是需要对checksum字段进行验证的,方法如下:
    1. 加入伪报头;
    2. 将(伪报头+UDP头+DATA)按16-bit分成若干个16-bit字,如果最后一个字节无法组成一个16-bit字,要在DATA的最后补0;
    3. 将所有的16-bit字相加(包括checksum字段),其结果仍然是16-bit字,如果出现溢出则结果+1;
    4. 如果最后结果为全1,即:0XFFFF,则表示UDP报文正确,否则应该认为UDP报文有错误,应该丢弃。

欢迎订阅 『网络编程专栏』


欢迎访问我的博客:https://whowin.cn

email: hengch@163.com

donation

1.2K views

More from this blog

双向链表及如何使用GLib的GList实现双向链表

双向链表是一种比单向链表更为灵活的数据结构,与单向链表相比可以有更多的应用场景,本文讨论双向链表的基本概念及实现方法,并着重介绍使用GLib的GList实现单向链表的方法及步骤,本文给出了多个实际范例源代码,旨在帮助学习基于GLib编程的读者较快地掌握GList的使用方法,本文程序在 ubuntu 20.04 下编译测试完成,gcc 版本号 9.4.0;本文适合初学者阅读。 1 双向链表及其实现 在文章《单向链表以及如何使用GLib中的GSList实现单向链表》中,介绍了单向链表以及基于 G...

Oct 29, 20245 min read24
双向链表及如何使用GLib的GList实现双向链表

C程序员应该知道的最好的8个c编程框架

C 编程框架是开发人员必不可少的工具,编程框架可以为构建强大且性能优异的应用程序提供结构化的基础,本文将对 8 个最佳 C 编程框架和库做出简要的介绍,如果您正在寻找适合初学者的 C 编程框架或旨在进行 C 编程框架比较,相信本文可以给您一定的帮助。 顶级 C 编程框架 – 概述 本文将介绍以下 8 个 C 语言编程框架: 序号框架名称主要特点易于集成下载链接 1GTK全面的小部件集,跨平台支持中等的下载 2Qt跨平台支持,集成开发环境中等的下载 3CMocka轻量级,模...

Oct 19, 20244 min read36
C程序员应该知道的最好的8个c编程框架

单向链表以及如何使用GLib中的GSList实现单向链表

单向链表是一种基础的数据结构,也是一种简单而灵活的数据结构,本文讨论单向链表的基本概念及实现方法,并着重介绍使用GLib的GSList实现单向链表的方法及步骤,本文给出了多个实际范例源代码,旨在帮助学习基于GLib编程的读者较快地掌握GSList的使用方法,本文程序在 ubuntu 20.04 下编译测试完成,gcc 版本号 9.4.0;本文适合初学者阅读。 1 单向链表及其实现 在文章《使用GLib进行C语言编程的实例》中,简单介绍了 GLib,建议阅读本文前先阅读这篇文章; 单向链表是一...

Aug 19, 20246 min read23
单向链表以及如何使用GLib中的GSList实现单向链表

使用GLib进行C语言编程的实例

本文将讨论使用GLib进行编程的基本步骤,GLib是一个跨平台的,用C语言编写的3个底层库(以前是5个)的集合,GLib提供了多种高级的数据结构,如内存块、双向和单向链表、哈希表等,GLib还实现了线程相关的函数、多线程编程以及相关的工具,例如原始变量访问、互斥锁、异步队列等,GLib主要由GNOME开发;本文是使用GLib编程的入门文章,旨在通过实例帮助希望学习GLib编程的读者较快地入门,本文将给出多个使用GLib库编程范例的源代码,本文程序在 ubuntu 20.04 下编译测试完成,gc...

Aug 9, 20245 min read9
使用GLib进行C语言编程的实例

Linux下使用libiw进行无线信号扫描的实例

打开电脑连接wifi是一件很平常的事情,但这些事情通常都是操作系统下的wifi管理程序替我们完成的,如何在程序中扫描wifi信号其实资料并不多,前面已经有两篇文章介绍了如何使用ioctl()扫描wifi信号,但其实在Linux下有一个简单的库对这些ioctl()的操作进行了封装,这个库就是libiw,使用libiw可以简化编程,本文介绍了如果使用libiw对wifi信号进行扫描的基本方法,本文将给出完整的源代码,本文程序在 ubuntu 20.04 下编译测试完成,gcc 版本号 9.4.0;尽...

Jul 4, 20244 min read21
Linux下使用libiw进行无线信号扫描的实例

whowin - 开源和分享是技术发展的源泉和动力

42 posts

一个从业30多年的退休程序员,主要从事嵌入式软件开发。

计算UDP报头的checksum