From: Etienne Dechamps Date: Sat, 5 Jul 2014 18:51:19 +0000 (+0100) Subject: Canonicalize IPv6 addresses as per RFC 5952 before printing them. X-Git-Tag: release-1.1pre11~80 X-Git-Url: https://tinc-vpn.org/git/browse?a=commitdiff_plain;h=d0d01a44485ee04f60a8fccf9bdf8311e23ffa43;p=tinc Canonicalize IPv6 addresses as per RFC 5952 before printing them. Currently we don't do any shortening on IPv6 addresses (aside from removing trailing zeroes) before printing them. This commit makes textual addresses smaller by shortening them according to the rules described in RFC 5952. This is also the canonical textual representation for IPv6 addresses, thus making them easier to compare. --- diff --git a/src/subnet_parse.c b/src/subnet_parse.c index b6e209ba..59228311 100644 --- a/src/subnet_parse.c +++ b/src/subnet_parse.c @@ -321,6 +321,8 @@ bool net2str(char *netstr, int len, const subnet_t *subnet) { subnet->net.mac.address.x[4], subnet->net.mac.address.x[5], subnet->weight); + netstr += result; + len -= result; break; case SUBNET_IPV4: @@ -329,32 +331,68 @@ bool net2str(char *netstr, int len, const subnet_t *subnet) { subnet->net.ipv4.address.x[1], subnet->net.ipv4.address.x[2], subnet->net.ipv4.address.x[3]); + netstr += result; + len -= result; prefixlength = subnet->net.ipv4.prefixlength; if (prefixlength == 32) prefixlength = -1; break; - case SUBNET_IPV6: - result = snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", - ntohs(subnet->net.ipv6.address.x[0]), - ntohs(subnet->net.ipv6.address.x[1]), - ntohs(subnet->net.ipv6.address.x[2]), - ntohs(subnet->net.ipv6.address.x[3]), - ntohs(subnet->net.ipv6.address.x[4]), - ntohs(subnet->net.ipv6.address.x[5]), - ntohs(subnet->net.ipv6.address.x[6]), - ntohs(subnet->net.ipv6.address.x[7])); + case SUBNET_IPV6: { + /* Find the longest sequence of consecutive zeroes */ + int max_zero_length = 0; + int max_zero_length_index = 0; + int current_zero_length = 0; + int current_zero_length_index = 0; + for (int i = 0; i < 8; i++) { + if (subnet->net.ipv6.address.x[i] != 0) + current_zero_length = 0; + else { + if (current_zero_length == 0) + current_zero_length_index = i; + current_zero_length++; + if (current_zero_length > max_zero_length) { + max_zero_length = current_zero_length; + max_zero_length_index = current_zero_length_index; + } + } + } + + /* Print the address */ + for (int i = 0; i < 8;) { + if (max_zero_length > 1 && max_zero_length_index == i) { + /* Shorten the representation as per RFC 5952 */ + const char* const FORMATS[] = { "%.1s", "%.2s", "%.3s" }; + const char* const* format = &FORMATS[0]; + if (i == 0) + format++; + if (i + max_zero_length == 8) + format++; + result = snprintf(netstr, len, *format, ":::"); + i += max_zero_length; + } else { + result = snprintf(netstr, len, "%hx:", ntohs(subnet->net.ipv6.address.x[i])); + i++; + } + netstr += result; + len -= result; + } + + /* Remove the trailing colon */ + netstr--; + len++; + *netstr = 0; + prefixlength = subnet->net.ipv6.prefixlength; if (prefixlength == 128) prefixlength = -1; break; + } default: logger(DEBUG_ALWAYS, LOG_ERR, "net2str() was called with unknown subnet type %d, exiting!", subnet->type); exit(1); } - netstr += result; - len -= result; if (prefixlength >= 0) { result = snprintf(netstr, len, "/%d", prefixlength);