在/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校验和是在整个有效负载上执行的,和标头中的其他字段,和 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是对此进行详细说明的规范。您可以在Google上搜索“ net-checksum.c Gerd Hoffmann”或在这里查看文件:
https://gist.github.com/fxlv/81209bbd150abfeaceb1f85ff076c9f3
您可以使用net_checksum_tcpudp
函数,向它提供UDP有效负载长度,协议,src和dst IP,然后再给UDP有效负载本身,它将做正确的事情。
最后,您必须在校验和上调用htons()
,这很好。
最后我找到了open-bsd dhclient packet.c:
https://github.com/openbsd/src/blob/master/sbin/dhclient/packet.c
签出功能assemble_udp_ip_header()