Generate a random IID if one is not specified.
- Add code to generate a random IPv6 address that's
checksum-neutral with the NAT64 prefix and clat IPv4 address.
- Only calculate the IP address after the NAT64 prefix is known.
- Because the clat IPv6 address is no longer determinisitic,
modify interface_poll so it checks whether the prefix has
changed instead of checking whether the IPv6 address has
changed.
- Add/update unit tests.
Change-Id: Ia53716ca5315ebdd0eaa3ad3a07552bf18e9dd5c
diff --git a/config.c b/config.c
index 623a1b0..09d3df0 100644
--- a/config.c
+++ b/config.c
@@ -31,6 +31,7 @@
#include "logging.h"
#include "getaddr.h"
#include "clatd.h"
+#include "checksum.h"
struct clat_config Global_Clatd_Config;
@@ -149,6 +150,16 @@
}
}
+/* function: ipv6_prefix_equal
+ * compares the prefixes two ipv6 addresses. assumes the prefix lengths are both /64.
+ * a1 - first address
+ * a2 - second address
+ * returns: 0 if the subnets are different, 1 if they are the same.
+ */
+int ipv6_prefix_equal(struct in6_addr *a1, struct in6_addr *a2) {
+ return !memcmp(a1, a2, 8);
+}
+
/* function: dns64_detection
* does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response with a query backoff timer
* net_id - (optional) netId to use, NETID_UNSET indicates use of default network
@@ -175,6 +186,28 @@
}
+void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet,
+ struct in6_addr *plat_subnet) {
+ // Fill last 8 bytes of IPv6 address with random bits.
+ arc4random_buf(&myaddr->s6_addr[8], 8);
+
+ // Make the IID checksum-neutral. That is, make it so that:
+ // checksum(Local IPv4 | Remote IPv4) = checksum(Local IPv6 | Remote IPv6)
+ // in other words (because remote IPv6 = NAT64 prefix | Remote IPv4):
+ // checksum(Local IPv4) = checksum(Local IPv6 | NAT64 prefix)
+ // Do this by adjusting the two bytes in the middle of the IID.
+
+ uint16_t middlebytes = (myaddr->s6_addr[11] << 8) + myaddr->s6_addr[12];
+
+ uint32_t c1 = ip_checksum_add(0, ipv4_local_subnet, sizeof(*ipv4_local_subnet));
+ uint32_t c2 = ip_checksum_add(0, plat_subnet, sizeof(*plat_subnet)) +
+ ip_checksum_add(0, myaddr, sizeof(*myaddr));
+
+ uint16_t delta = ip_checksum_adjust(middlebytes, c1, c2);
+ myaddr->s6_addr[11] = delta >> 8;
+ myaddr->s6_addr[12] = delta & 0xff;
+}
+
/* function: config_generate_local_ipv6_subnet
* generates the local ipv6 subnet when given the interface ip
* requires config.ipv6_host_id
@@ -183,8 +216,16 @@
void config_generate_local_ipv6_subnet(struct in6_addr *interface_ip) {
int i;
- for(i = 2; i < 4; i++) {
- interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i];
+ if (IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_host_id)) {
+ /* Generate a random interface ID. */
+ gen_random_iid(interface_ip,
+ &Global_Clatd_Config.ipv4_local_subnet,
+ &Global_Clatd_Config.plat_subnet);
+ } else {
+ /* Use the specified interface ID. */
+ for(i = 2; i < 4; i++) {
+ interface_ip->s6_addr32[i] = Global_Clatd_Config.ipv6_host_id.s6_addr32[i];
+ }
}
}
@@ -195,10 +236,12 @@
*/
int subnet_from_interface(cnode *root, const char *interface) {
union anyip *interface_ip;
+ char addrstr[INET6_ADDRSTRLEN];
- if(!config_item_ip6(root, "ipv6_host_id", "::464", &Global_Clatd_Config.ipv6_host_id))
+ if(!config_item_ip6(root, "ipv6_host_id", "::", &Global_Clatd_Config.ipv6_host_id))
return 0;
+ // TODO: check that the prefix length is /64.
interface_ip = getinterface_ip(interface, AF_INET6);
if(!interface_ip) {
logmsg(ANDROID_LOG_FATAL,"unable to find an ipv6 ip on interface %s",interface);
@@ -210,6 +253,9 @@
config_generate_local_ipv6_subnet(&Global_Clatd_Config.ipv6_local_subnet);
+ inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr));
+ logmsg(ANDROID_LOG_INFO, "Using %s on %s", addrstr, interface);
+
return 1;
}
@@ -240,9 +286,6 @@
strncpy(Global_Clatd_Config.default_pdp_interface, uplink_interface, sizeof(Global_Clatd_Config.default_pdp_interface));
- if(!subnet_from_interface(root,Global_Clatd_Config.default_pdp_interface))
- goto failed;
-
if(!config_item_int16_t(root, "mtu", "-1", &Global_Clatd_Config.mtu))
goto failed;
@@ -275,6 +318,9 @@
}
}
+ if(!subnet_from_interface(root,Global_Clatd_Config.default_pdp_interface))
+ goto failed;
+
return 1;