4

我使用了 ASIO 文档中提供的 ICMP示例来创建一个简单的 ping 实用程序。但是,该示例仅涵盖 IPv4,我很难使其与 IPv6 一起使用。

升级 ICMP 标头类以支持 IPv6 需要进行微小的更改 - ICMP 和 ICMPv6 标头之间的唯一区别是 ICMP 类型的不同枚举。但是,我在计算需要包含在 ICMPv6 标头中的校验和时遇到问题。

对于 IPv4,校验和基于 ICMP 标头和有效负载。但是,对于 IPv6,校验和应在 ICMPv6 标头和有效负载之前包含 IPv6 伪标头。ICMPv6 校验和功能需要知道 IPv6 标头中的源地址和目标地址。但是,我们无法控制 IPv6 标头中的内容。在 Asio-Boost 中如何做到这一点?

作为参考,请在下面找到用于 IPv4 校验和计算的函数。

void compute_checksum(icmp_header& header, Iterator body_begin, Iterator body_end)
{
  unsigned int sum = (header.type() << 8) + header.code()
    + header.identifier() + header.sequence_number();

  Iterator body_iter = body_begin;
  while (body_iter != body_end)
  {
    sum += (static_cast<unsigned char>(*body_iter++) << 8);
    if (body_iter != body_end)
    sum += static_cast<unsigned char>(*body_iter++);
  }

  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);
  header.checksum(static_cast<unsigned short>(~sum));
}

[编辑]

如果校验和计算不正确会有什么后果?如果回显请求的校验和无效,目标主机会发送回显应答吗?

4

1 回答 1

1

如果校验和不正确,典型的 IPv6 实现将丢弃数据包。所以,这是一个严重的问题。

如果您坚持自己制作数据包,则必须完全完成。这包括查找源 IP 地址,在计算校验和之前将其放入伪标头中。这是我在 C 中的做法,通过为我的预期目标地址调用 connect()(即使我使用 UDP,所以它应该适用于 ICMP):

     /* Get the source IP addresse chosen by the system (for verbose display, and 
     * for checksumming) */
    if (connect(sd, destination->ai_addr, destination->ai_addrlen) < 0) {
        fprintf(stderr, "Cannot connect the socket: %s\n", strerror(errno));
        abort();
    }
    source = malloc(sizeof(struct addrinfo));
    source->ai_addr = malloc(sizeof(struct sockaddr_storage));
    source_len = sizeof(struct sockaddr_storage);
    if (getsockname(sd, source->ai_addr, &source_len) < 0) {
        fprintf(stderr, "Cannot getsockname: %s\n", strerror(errno));
        abort();
    }

然后,稍后:

        sockaddr6 = (struct sockaddr_in6 *) source->ai_addr;
        op6.ip.ip6_src = sockaddr6->sin6_addr;

和:

        op6.udp.check =
            checksum6(op6.ip, op6.udp, (u_int8_t *) & message, messagesize);
于 2013-06-15T21:29:34.370 回答