/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 标头加上它后面的有效负载?)
谢谢。
/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 位值执行补码和,然后取该值的补码(即反转所有位)以填充校验和字段(附加条件是计算的校验和为零将变为所有一位)。
一个的补码和不仅仅是所有一个补码值的总和。这有点复杂。
基本上,您有一个从零开始运行的 16 位累加器,然后将每个 16 位值加到其中。每当这些加法中的一个导致进位时,该值就会被环绕,然后您再次将值加到该值上。这有效地获取了 16 位加法的进位位并将其添加到值中。
顺便说一句,这纯粹是我的猜想,但这可能可以通过使用
ADC
(带进位加法)指令而不是ADD
(令人惊讶的是,加法)或当时您的 CPU 上可用的任何等效指令来有效地完成.如果没有进位,
ADC
则只需从进位中添加零位。在这些东西完成的日子里(是的,不幸的是,我已经那么老了),内存比速度更受限制,现在情况并非如此,所以在你的代码中节省几个字节可以很好地提升你宇宙半神帝的水平:-)
请注意,您不必担心第二次进位(或者ADC
如果您使用上一段中提到的方法,则下一次进位是两个),因为两个最大的 16 位值相加时会产生(截断from 0x1fffe
) 0xfffe
- 添加一个永远不会导致另一个进位。
一旦计算出的补码和被计算,将其位反转并插入数据包中,这将导致接收端的计算产生0xffff
,当然假设传输中没有错误。
值得注意的是,有效载荷总是被填充以确保有整数个 16 位字。如果它被填充,长度字段会告诉您实际长度。
RFC768是详细说明这一点的规范。
Gerd Hoffmann 完成了一个很好且易于理解的 UDP 校验和计算示例。
您可以在 Google 上搜索“net-checksum.c Gerd Hoffmann”或查看此处的文件:
https://gist.github.com/fxlv/81209bbd150abfeaceb1f85ff076c9f3
您可以使用net_checksum_tcpudp
函数,向其提供 UDP 有效负载长度、proto、src 和 dst IP,然后输入 UDP 有效负载本身,它会做正确的事情。
最后你必须调用htons()
校验和,你很好。
我在网上搜索一些将计算 udp 标头的代码(使用上面提到的伪 ip 标头)。
最后我找到了open-bsd dhclient packet.c:
https://github.com/openbsd/src/blob/master/sbin/dhclient/packet.c
检查功能assemble_udp_ip_header()