/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头加上它后面的有效负载吗?)
感谢。
答案 0 :(得分:34)
UDP校验和在整个有效负载上执行,和标头中的其他字段,和来自IP标头的某些字段。从IP报头构造伪报头以便执行计算(通过该伪报头,UDP报头和有效载荷完成)。包含伪标头的原因是捕获已路由到错误IP地址的数据包。
基本上,在接收端,标题的所有16位字加数据区被加在一起(以16位包裹),并且结果将与0xffff
进行核对。
在发送方面,它有点复杂。在所有16位值上执行一个补码和,然后从该值获取一个补码(即,反转所有位)以填充校验和字段(具有计算的校验和为零的额外条件将变为全部一个位)。
一个补码和不只是所有一个补码值的总和。它有点复杂。
基本上,你有一个从零开始运行的16位累加器,你将每个16位值加到那里。每当其中一个添加产生进位时,该值就会被包围,并再次向该值添加一个值。这有效地取得了16位加法的进位,并将其加到值上。
顺便说一句,这对我来说是纯粹的猜想,但这可能通过使用
ADC
(with with carry)指令而不是ADD
(令人惊讶的是,添加)来有效地完成。 ,或者当时CPU上可用的任何等效指令。如果没有进位,
ADC
只会从进位中添加零位。在这些东西完成的时候(是的,不幸的是,我是那么旧),内存远远超过了速度,而不是现在的情况,所以节省了几个字节你的代码可以很好地将你提升到宇宙的半神 - 皇帝的水平: - )
请注意,您永远不必担心第二次携带(或者如果您正在使用前一段中提到的方法,则使用下一个ADC
进行两次携带)因为两个最大的16位值,总和时,生成(从0x1fffe
截断)0xfffe
- 在其中添加一个将永远不会导致另一个进位。
一旦计算出的一个补码和,它的位被反转并插入到数据包中,这将导致接收端的计算产生0xffff
,假设传输中没有错误。
值得注意的是,有效载荷总是被填充以确保有16位字的整数。如果 填充,则长度字段会告诉您实际的长度。
RFC768是详细说明的规范。
答案 1 :(得分:1)
我正在网上搜索一些会计算udp标头的代码(如上所述使用伪ip标头)。
最后我找到了open-bsd dhclient packet.c:
https://github.com/DragonFlyBSD/DragonFlyBSD/blob/master/sbin/dhclient/packet.c
查看函数assemble_udp_ip_header()
答案 2 :(得分:1)
一个很好且易于理解的UDP校验和计算示例由Gerd Hoffmann完成。
你可以谷歌搜索“net-checksum.c Gerd Hoffmann” 或者在这里查看文件:
https://gist.github.com/fxlv/81209bbd150abfeaceb1f85ff076c9f3
您可以使用net_checksum_tcpudp
函数,将UDP有效负载长度,proto,src和dst IP以及UDP有效负载本身提供给它,它将做正确的事情。
最后你必须在校验和上打电话给htons()
并且你很好。