UDP校验和计算

问题描述 投票:21回答:3

在/usr/include/netinet/udp.h定义的UDP头结构如下

struct udphdr
{
  u_int16_t source;
  u_int16_t dest;
  u_int16_t len;
  u_int16_t check;
};

标题的检查字段中存储了什么值?如何验证校验和是否正确?我的意思是校验和是基于什么数据计算的? (是只是udp标头还是udp标头加上其后的有效负载?)

谢谢。

udp checksum
3个回答
35
投票

UDP校验和是在整个有效负载上执行的,标头中的其他字段, IP标头中的某些字段。从IP报头构造一个伪报头以便执行计算(在此伪报头,UDP报头和有效负载上完成)。包含伪头的原因是为了捕获已路由到错误IP地址的数据包。

[基本上,在接收端,标头的所有16位字加上数据区被加在一起(以16位换行),并根据0xffff检查结果。

在发送方,这有点复杂。对所有16位值执行一个人的补码和,然后对该值进行一个人的补码(即,将所有位取反)以填充校验和字段(额外的条件是将计算出的校验和0更改为全部一比特)。

一个人的补码和为不是只是一个人的补码值之和。有点复杂。

[基本上,您有一个从零开始的正在运行的16位累加器,然后将每个16位值加到该累加器上。每当这些加法之一导致进位时,该值都会被换位,然后您将一个再次加到该值上。这有效地获取了16位加法的进位位并将其添加到值中。


顺便说一句,这纯粹是我的推测,但是这可以通过使用ADC(带进位加法)指令而不是ADD(令人惊讶的是,加法)或任何等效指令来有效地完成当时在您的CPU上可用。

[如果没有进位,ADC只会从进位中加零。在完成这些工作的日子里(是的,很不幸,我am那么旧),内存远不是速度的限制,而是速度的限制,如今的情况已不再如此,因此在代码中保存一些字节可以很好地将您提升到“半神半成品”的水平:-)


请注意,您不必担心第二次进位(如果使用的是上段中提到的方法,则不必担心第二次进位ADC将进位两次),因为两个最大的16位值总计,产生(从0x1fffe截断)0xfffe-在其中加上一个将永远不会引起另一进位。

一旦计算出所求和的补码和,将其比特取反并插入到分组中,这将导致在接收端的计算产生0xffff,前提是当然不会发生传输错误。

值得注意的是,总是对有效负载进行填充,以确保存在整数个16位字。如果填充了[[was,则长度字段会告诉您实际的长度。

RFC768是对此进行详细说明的规范。

1
投票
[一个不错的,易于理解的UDP校验和计算示例由Gerd Hoffmann完成。

您可以在Google上搜索“ net-checksum.c Gerd Hoffmann”或在这里查看文件:

https://gist.github.com/fxlv/81209bbd150abfeaceb1f85ff076c9f3

您可以使用net_checksum_tcpudp函数,向它提供UDP有效负载长度,协议,src和dst IP,然后再给UDP有效负载本身,它将做正确的事情。

最后,您必须在校验和上调用htons(),这很好。


0
投票
我正在网上搜索一些代码,这些代码将计算udp头(具有如上所述的伪ip头)。

最后我找到了open-bsd dhclient packet.c:

https://github.com/openbsd/src/blob/master/sbin/dhclient/packet.c

签出功能assemble_udp_ip_header()

© www.soinside.com 2019 - 2024. All rights reserved.