From d0d01a44485ee04f60a8fccf9bdf8311e23ffa43 Mon Sep 17 00:00:00 2001 From: Etienne Dechamps Date: Sat, 5 Jul 2014 19:51:19 +0100 Subject: [PATCH] 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. --- src/subnet_parse.c | 62 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 12 deletions(-) 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); -- 2.20.1