【问题标题】:UDP checksum calculationUDP校验和计算
【发布时间】:2010-12-01 14:56:06
【问题描述】:

在/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头加上后面的payload?)

谢谢。

【问题讨论】:

    标签: c network-programming udp checksum


    【解决方案1】:

    我正在网上搜索一些可以计算 udp 标头的代码(使用上面提到的伪 ip 标头)。

    终于找到了open-bsd dhclient packet.c:

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

    查看函数assemble_udp_ip_header()

    【讨论】:

    【解决方案2】:

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

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

    在发送端,它有点复杂。对所有 16 位值执行补码和,然后取该值的补码(即反转所有位)以填充校验和字段(附加条件是计算的校验和为零将变为所有一位)。

    一个人的补码和只是所有一个人的补码值的总和。这有点复杂。

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


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

    如果没有进位,ADC 只会从进位中添加零位。在这些东西完成的日子里(是的,不幸的是,我那么老了),内存比速度更受限制,现在情况并非如此,所以节省了几个字节你的代码很可能把你提升到宇宙的半神帝王的水平:-)


    请注意,由于两个最大的 16 位值,您不必担心第二次进位(或者如果您使用上一段中提到的方法,则下一个 ADC 进位是两个),求和时,产生(从0x1fffe 截断)0xfffe - 添加一个永远不会导致另一个进位。

    一旦计算出的补码和,将其位反转并插入数据包中,这将导致接收端的计算产生0xffff,当然假设传输没有错误。

    值得注意的是,负载总是被填充以确保有整数个 16 位字。如果它填充,长度字段会告诉你实际长度。

    RFC768 是详细说明这一点的规范。

    【讨论】:

    • 谢谢。我对 Pseudo-Header 部分有点困惑..但随后 RFC 清除了空气。
    • “所有的 16 位字头(UDP 校验和为零)都被添加,而一个补码(即,反转所有位)被放入校验和字段。”不,RFC 所说的会导致“添加标头的 16 位字(其中 UDP 校验和为零)的所有补码(即,反转所有位),然后将其补码放入进入校验和字段。”否则完美答案+1。
    • 这是不对的。 “补码和”并不意味着将每个单词的补码相加。这意味着您将单词加在一起,并且当产生进位位时,您将运行总和加 1。见mathforum.org/library/drmath/view/54379.html
    • 谢谢@Jim,更多的研究表明你是对的。我本来想说我错了,但我的自我在最后一刻介入并不允许它:-)。如果您认为更新后的答案仍然错误,请告诉我。干杯。
    • 关于补码校验和的一个有趣的注释,这可能与选择它的原因有很大关系,即交换要求和的单词的字节顺序将交换总和的字节顺序.
    【解决方案3】:

    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(),你很好。

    【讨论】:

      猜你喜欢
      • 2014-08-16
      • 2010-12-18
      • 1970-01-01
      • 2015-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-21
      • 1970-01-01
      相关资源
      最近更新 更多