summaryrefslogtreecommitdiffstats
path: root/config.c
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2014-10-31 21:54:33 +0900
committerLorenzo Colitti <lorenzo@google.com>2014-11-01 01:17:22 +0900
commit798f9934fca523dfb57136bd185cf6e9460323ad (patch)
tree2173d37b9a340b20d06a339375e9b27a7730b01c /config.c
parent8a41a5d140b3cf56a54bdeef234e89ee12cba0dc (diff)
downloadplatform_external_android-clat-798f9934fca523dfb57136bd185cf6e9460323ad.tar.gz
platform_external_android-clat-798f9934fca523dfb57136bd185cf6e9460323ad.tar.bz2
platform_external_android-clat-798f9934fca523dfb57136bd185cf6e9460323ad.zip
Use different IPv4 addresses on different clat interfaces.
Previously the code would use 192.0.0.4 on all clat interfaces. This works, but it has the problem when a clat interface goes down, we do not reset TCP connections on its IP address if there is another clat interface up. Fix this by assigning every clat interface its own IP address in 192.0.0.0/29. Bug: 12111730 Change-Id: I28c831acd93b0980efea8e90c1cdf8b607beac68
Diffstat (limited to 'config.c')
-rw-r--r--config.c70
1 files changed, 68 insertions, 2 deletions
diff --git a/config.c b/config.c
index 4939478..de8a26f 100644
--- a/config.c
+++ b/config.c
@@ -185,7 +185,12 @@ void dns64_detection(unsigned net_id) {
}
}
-
+/* function: gen_random_iid
+ * picks a random interface ID that is checksum neutral with the IPv4 address and the NAT64 prefix
+ * myaddr - IPv6 address to write to
+ * ipv4_local_subnet - clat IPv4 address
+ * plat_subnet - NAT64 prefix
+ */
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.
@@ -208,6 +213,61 @@ void gen_random_iid(struct in6_addr *myaddr, struct in_addr *ipv4_local_subnet,
myaddr->s6_addr[12] = delta & 0xff;
}
+// Factored out to a separate function for testability.
+int connect_is_ipv4_address_free(in_addr_t addr) {
+ int s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1) {
+ return 0;
+ }
+
+ // Attempt to connect to the address. If the connection succeeds and getsockname returns the same
+ // the address then the address is already assigned to the system and we can't use it.
+ struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr = { addr }, .sin_port = 53 };
+ socklen_t len = sizeof(sin);
+ int inuse = connect(s, (struct sockaddr *) &sin, sizeof(sin)) == 0 &&
+ getsockname(s, (struct sockaddr *) &sin, &len) == 0 &&
+ (size_t) len >= sizeof(sin) &&
+ sin.sin_addr.s_addr == addr;
+
+ close(s);
+ return !inuse;
+}
+
+addr_free_func config_is_ipv4_address_free = connect_is_ipv4_address_free;
+
+/* function: config_select_ipv4_address
+ * picks a free IPv4 address, starting from ip and trying all addresses in the prefix in order
+ * ip - the IP address from the configuration file
+ * prefixlen - the length of the prefix from which addresses may be selected.
+ * returns: the IPv4 address, or INADDR_NONE if no addresses were available
+ */
+in_addr_t config_select_ipv4_address(const struct in_addr *ip, int16_t prefixlen) {
+ in_addr_t chosen = INADDR_NONE;
+
+ // Don't accept prefixes that are too large because we scan addresses one by one.
+ if (prefixlen < 16 || prefixlen > 32) {
+ return chosen;
+ }
+
+ // All these are in host byte order.
+ in_addr_t mask = 0xffffffff >> (32 - prefixlen) << (32 - prefixlen);
+ in_addr_t ipv4 = ntohl(ip->s_addr);
+ in_addr_t first_ipv4 = ipv4;
+ in_addr_t prefix = ipv4 & mask;
+
+ // Pick the first IPv4 address in the pool, wrapping around if necessary.
+ // So, for example, 192.0.0.4 -> 192.0.0.5 -> 192.0.0.6 -> 192.0.0.7 -> 192.0.0.0.
+ do {
+ if (config_is_ipv4_address_free(htonl(ipv4))) {
+ chosen = htonl(ipv4);
+ break;
+ }
+ ipv4 = prefix | ((ipv4 + 1) & ~mask);
+ } while (ipv4 != first_ipv4);
+
+ return chosen;
+}
+
/* function: config_generate_local_ipv6_subnet
* generates the local ipv6 subnet when given the interface ip
* requires config.ipv6_host_id
@@ -264,7 +324,12 @@ int read_config(const char *file, const char *uplink_interface, const char *plat
if(!config_item_int16_t(root, "ipv4mtu", "-1", &Global_Clatd_Config.ipv4mtu))
goto failed;
- if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet))
+ if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET,
+ &Global_Clatd_Config.ipv4_local_subnet))
+ goto failed;
+
+ if(!config_item_int16_t(root, "ipv4_local_prefixlen", DEFAULT_IPV4_LOCAL_PREFIXLEN,
+ &Global_Clatd_Config.ipv4_local_prefixlen))
goto failed;
if(plat_prefix) { // plat subnet is coming from the command line
@@ -311,6 +376,7 @@ void dump_config() {
logmsg(ANDROID_LOG_DEBUG,"ipv4mtu = %d",Global_Clatd_Config.ipv4mtu);
logmsg(ANDROID_LOG_DEBUG,"ipv6_local_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer)));
logmsg(ANDROID_LOG_DEBUG,"ipv4_local_subnet = %s",inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer)));
+ logmsg(ANDROID_LOG_DEBUG,"ipv4_local_prefixlen = %d", Global_Clatd_Config.ipv4_local_prefixlen);
logmsg(ANDROID_LOG_DEBUG,"plat_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer)));
logmsg(ANDROID_LOG_DEBUG,"default_pdp_interface = %s",Global_Clatd_Config.default_pdp_interface);
}