Question

I have used the ICMP example provided in the ASIO documentation to create a simple ping utility. However, the example covers IPv4 only and I have a hard time to make it work with IPv6.

Upgrading the ICMP header class to support IPv6 requires a minor change - the only difference between ICMP and ICMPv6 header is the different enumeration of ICMP types. However, I have a problem computing the checksum that needs to be incorporated in the ICMPv6 header.

For IPv4 the checksum is based on the ICMP header and payload. However, for IPv6 the checksum should include the IPv6 pseudo-header before the ICMPv6 header and payload. The ICMPv6 checksum function needs to know the source and destination address that will be in the IPv6 header. However, we have no control over what goes into the IPv6 header. How can this be done in Asio-Boost?

For reference please find below the function for IPv4 checksum calculation.

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));
}

[EDIT]

What are the consequences if the checksum is not calculated correctly? Will the target host send echo reply if the echo request has invalid checksum?

Was it helpful?

Solution

If the checksum is incorrect, a typical IPv6 implementation will drop the packet. So, it is a serious issue.

If you insist on crafting the packet yourself, you'll have to do it completely. This incldues finding the source IP address, to put it in the pseudo-header before computing the checksum. Here is how I do it in C, by calling connect() for my intended destination address (even when I use UDP, so it should work for 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();
    }

then, later:

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

and:

        op6.udp.check =
            checksum6(op6.ip, op6.udp, (u_int8_t *) & message, messagesize);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top