summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2019-01-11 18:10:11 +0900
committerLorenzo Colitti <lorenzo@google.com>2019-01-15 13:33:36 +0900
commitf0fac8631a58ee6f12755e35f0476bef3f9bbb97 (patch)
tree2c211b29071463e15af4273b0d061f3b14d080e3
parent33351930039785304a283b072251a48cfae30a35 (diff)
downloadplatform_external_android-clat-f0fac8631a58ee6f12755e35f0476bef3f9bbb97.tar.gz
platform_external_android-clat-f0fac8631a58ee6f12755e35f0476bef3f9bbb97.tar.bz2
platform_external_android-clat-f0fac8631a58ee6f12755e35f0476bef3f9bbb97.zip
Allow passing in the IPv4 and IPv6 addresses on the command line.
This allows the address to be determined by netd and passed in, which makes it possible for the rest of the OS to know what the clatd IPv6 address is. Bug: 65674744 Test: atest clatd_test Test: IPv4 on IPv6-only networks continues to work Test: clatd continues to avoid existing IPv4 addresses in 192.0.0.0/29 Test: passing NAT64 prefix, IPv4/IPv6 addresses on command line works Change-Id: I8519c2f01b44022ef036c80aa0df32cd76003055
-rw-r--r--clatd.c110
-rw-r--r--clatd.h8
-rw-r--r--clatd_test.cpp112
-rw-r--r--main.c19
4 files changed, 207 insertions, 42 deletions
diff --git a/clatd.c b/clatd.c
index bcbd9c5..3ccceea 100644
--- a/clatd.c
+++ b/clatd.c
@@ -110,13 +110,11 @@ int configure_packet_socket(int sock) {
return 1;
}
-/* function: configure_tun_ip
- * configures the ipv4 and ipv6 addresses on the tunnel interface
- * tunnel - tun device data
+/* function: ipv4_address_generate
+ * picks a free IPv4 address from the local subnet or exits if there are no free addresses
+ * returns: the IPv4 address as an in_addr_t
*/
-void configure_tun_ip(const struct tun_data *tunnel) {
- int status;
-
+static in_addr_t ipv4_address_generate() {
// Pick an IPv4 address to use by finding a free address in the configured prefix. Technically,
// there is a race here - if another clatd calls config_select_ipv4_address after we do, but
// before we call add_address, it can end up having the same IP address as we do. But the time
@@ -131,22 +129,49 @@ void configure_tun_ip(const struct tun_data *tunnel) {
Global_Clatd_Config.ipv4_local_prefixlen);
exit(1);
}
- Global_Clatd_Config.ipv4_local_subnet.s_addr = localaddr;
+ return localaddr;
+}
- // Configure the interface before bringing it up. As soon as we bring the interface up, the
- // framework will be notified and will assume the interface's configuration has been finalized.
- status = add_address(tunnel->device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, 32,
- &Global_Clatd_Config.ipv4_local_subnet);
- if (status < 0) {
- logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_address(4) failed: %s", strerror(-status));
+/* function: ipv4_address_from_cmdline
+ * configures the IPv4 address specified on the command line, or exits if the address is not valid
+ * v4_addr - a string, the IPv4 address
+ * returns: the IPv4 address as an in_addr_t
+ */
+static in_addr_t ipv4_address_from_cmdline(const char *v4_addr) {
+ in_addr_t localaddr;
+ if (!inet_pton(AF_INET, v4_addr, &localaddr)) {
+ logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr);
exit(1);
}
+ return localaddr;
+}
+
+/* function: configure_tun_ip
+ * configures the ipv4 and ipv6 addresses on the tunnel interface
+ * tunnel - tun device data
+ */
+void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr) {
+ if (v4_addr) {
+ Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_from_cmdline(v4_addr);
+ } else {
+ Global_Clatd_Config.ipv4_local_subnet.s_addr = ipv4_address_generate();
+ }
char addrstr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, addrstr, sizeof(addrstr));
logmsg(ANDROID_LOG_INFO, "Using IPv4 address %s on %s", addrstr, tunnel->device4);
- if ((status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu)) < 0) {
+ // Configure the interface before bringing it up. As soon as we bring the interface up, the
+ // framework will be notified and will assume the interface's configuration has been finalized.
+ int status = add_address(tunnel->device4, AF_INET, &Global_Clatd_Config.ipv4_local_subnet, 32,
+ &Global_Clatd_Config.ipv4_local_subnet);
+ if (status < 0) {
+ logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_address(4) failed: %s", strerror(-status));
+ exit(1);
+ }
+
+ status = if_up(tunnel->device4, Global_Clatd_Config.ipv4mtu);
+ if (status < 0) {
logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_up(4) failed: %s", strerror(-status));
exit(1);
}
@@ -251,15 +276,13 @@ int ipv6_address_changed(const char *interface) {
}
}
-/* function: configure_clat_ipv6_address
- * picks the clat IPv6 address and configures packet translation to use it.
- * tunnel - tun device data
+/* function: clat_ipv6_address_from_interface
+ * picks the clat IPv6 address based on the interface address
* interface - uplink interface name
* returns: 1 on success, 0 on failure
*/
-int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface) {
+static int clat_ipv6_address_from_interface(const char *interface) {
union anyip *interface_ip;
- char addrstr[INET6_ADDRSTRLEN];
// TODO: check that the prefix length is /64.
interface_ip = getinterface_ip(interface, AF_INET6);
@@ -270,13 +293,48 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter
// Generate an interface ID.
config_generate_local_ipv6_subnet(&interface_ip->ip6);
- inet_ntop(AF_INET6, &interface_ip->ip6, addrstr, sizeof(addrstr));
+
+ Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6;
+ free(interface_ip);
+ return 1;
+}
+
+/* function: clat_ipv6_address_from_cmdline
+ * parses the clat IPv6 address from the command line
+ * v4_addr - a string, the IPv6 address
+ * returns: 1 on success, 0 on failure
+ */
+static int clat_ipv6_address_from_cmdline(const char *v6_addr) {
+ if (!inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) {
+ logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* function: configure_clat_ipv6_address
+ * picks the clat IPv6 address and configures packet translation to use it.
+ * tunnel - tun device data
+ * interface - uplink interface name
+ * returns: 1 on success, 0 on failure
+ */
+int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface,
+ const char *v6_addr) {
+ int ret;
+ if (v6_addr) {
+ ret = clat_ipv6_address_from_cmdline(v6_addr);
+ } else {
+ ret = clat_ipv6_address_from_interface(interface);
+ }
+ if (!ret) return 0;
+
+ char addrstr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, addrstr, sizeof(addrstr));
logmsg(ANDROID_LOG_INFO, "Using IPv6 address %s on %s", addrstr, interface);
// Start translating packets to the new prefix.
- Global_Clatd_Config.ipv6_local_subnet = interface_ip->ip6;
add_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, interface);
- free(interface_ip);
// Update our packet socket filter to reflect the new 464xlat IP address.
if (!configure_packet_socket(tunnel->read_fd6)) {
@@ -295,8 +353,8 @@ int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *inter
* tunnel - tun device data
* net_id - NetID to use, NETID_UNSET indicates use of default network
*/
-void configure_interface(const char *uplink_interface, const char *plat_prefix,
- struct tun_data *tunnel, unsigned net_id) {
+void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr,
+ const char *v6_addr, struct tun_data *tunnel, unsigned net_id) {
int error;
if (!read_config("/system/etc/clatd.conf", uplink_interface, plat_prefix, net_id)) {
@@ -335,9 +393,9 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix,
exit(1);
}
- configure_tun_ip(tunnel);
+ configure_tun_ip(tunnel, v4_addr);
- if (!configure_clat_ipv6_address(tunnel, uplink_interface)) {
+ if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) {
exit(1);
}
}
diff --git a/clatd.h b/clatd.h
index 67ba990..f7f7315 100644
--- a/clatd.h
+++ b/clatd.h
@@ -35,13 +35,15 @@ struct tun_data;
#define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90
void stop_loop();
+void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr);
void set_capability(uint64_t target_cap);
void drop_root_but_keep_caps();
void open_sockets(struct tun_data *tunnel, uint32_t mark);
int ipv6_address_changed(const char *interface);
-int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface);
-void configure_interface(const char *uplink_interface, const char *plat_prefix,
- struct tun_data *tunnel, unsigned net_id);
+int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface,
+ const char *src_addr);
+void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr,
+ const char *v6, struct tun_data *tunnel, unsigned net_id);
void event_loop(struct tun_data *tunnel);
int parse_unsigned(const char *str, unsigned *out);
diff --git a/clatd_test.cpp b/clatd_test.cpp
index 81af41d..7705a36 100644
--- a/clatd_test.cpp
+++ b/clatd_test.cpp
@@ -294,8 +294,8 @@ void check_packet(const uint8_t *packet, size_t len, const char *msg) {
void reassemble_packet(const uint8_t **fragments, const size_t lengths[], int numpackets,
uint8_t *reassembled, size_t *reassembled_len, const char *msg) {
- struct iphdr *ip = NULL;
- struct ip6_hdr *ip6 = NULL;
+ struct iphdr *ip = nullptr;
+ struct ip6_hdr *ip6 = nullptr;
size_t total_length, pos = 0;
uint8_t protocol = 0;
uint8_t version = ip_version(fragments[0]);
@@ -560,6 +560,21 @@ int get_transport_checksum(const uint8_t *packet) {
}
}
+static tun_data makeTunData() {
+ // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk.
+ return {
+ .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW),
+ .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)),
+ .fd4 = socket(AF_UNIX, SOCK_DGRAM, 0),
+ };
+}
+
+void freeTunData(tun_data *tunnel) {
+ close(tunnel->write_fd6);
+ close(tunnel->read_fd6);
+ close(tunnel->fd4);
+}
+
struct clat_config Global_Clatd_Config;
class ClatdTest : public ::testing::Test {
@@ -757,6 +772,55 @@ TEST_F(ClatdTest, SelectIPv4Address) {
EXPECT_EQ(inet_addr("127.0.0.2"), config_select_ipv4_address(&addr, 29));
}
+TEST_F(ClatdTest, ConfigureTunIp) {
+ addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free;
+ config_is_ipv4_address_free = over6_free;
+
+ Global_Clatd_Config.ipv4_local_prefixlen = 29;
+ Global_Clatd_Config.ipv4mtu = 1472;
+
+ // Create an interface for configure_tun_ip to configure and bring up.
+ TunInterface v4Iface;
+ ASSERT_EQ(0, v4Iface.init());
+ struct tun_data tunnel = makeTunData();
+ strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4));
+
+ configure_tun_ip(&tunnel, nullptr /* v4_addr */);
+ EXPECT_EQ(inet_addr("192.0.0.6"), Global_Clatd_Config.ipv4_local_subnet.s_addr);
+
+ union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET);
+ EXPECT_EQ(inet_addr("192.0.0.6"), ip->ip4.s_addr);
+ free(ip);
+
+ config_is_ipv4_address_free = orig_config_is_ipv4_address_free;
+ v4Iface.destroy();
+}
+
+TEST_F(ClatdTest, ConfigureTunIpManual) {
+ addr_free_func orig_config_is_ipv4_address_free = config_is_ipv4_address_free;
+ config_is_ipv4_address_free = over6_free;
+
+ Global_Clatd_Config.ipv4_local_prefixlen = 29;
+ Global_Clatd_Config.ipv4mtu = 1472;
+
+ // Create an interface for configure_tun_ip to configure and bring up.
+ TunInterface v4Iface;
+ ASSERT_EQ(0, v4Iface.init());
+ struct tun_data tunnel = makeTunData();
+ strlcpy(tunnel.device4, v4Iface.name().c_str(), sizeof(tunnel.device4));
+
+ configure_tun_ip(&tunnel, "192.0.2.1" /* v4_addr */);
+ EXPECT_EQ(inet_addr("192.0.2.1"), Global_Clatd_Config.ipv4_local_subnet.s_addr);
+
+ union anyip *ip = getinterface_ip(v4Iface.name().c_str(), AF_INET);
+ ASSERT_NE(nullptr, ip);
+ EXPECT_EQ(inet_addr("192.0.2.1"), ip->ip4.s_addr);
+ free(ip);
+
+ config_is_ipv4_address_free = orig_config_is_ipv4_address_free;
+ v4Iface.destroy();
+}
+
TEST_F(ClatdTest, DataSanitycheck) {
// Sanity checks the data.
uint8_t v4_header[] = { IPV4_UDP_HEADER };
@@ -965,16 +1029,22 @@ TEST_F(ClatdTest, GetInterfaceIp) {
expect_ipv6_addr_equal(&expected, &actual);
}
+void expectSocketBound(int ifindex, int sock) {
+ // Check that the packet socket is bound to the interface. We can't check the socket filter
+ // because there is no way to fetch it from the kernel.
+ sockaddr_ll sll;
+ socklen_t len = sizeof(sll);
+ ASSERT_EQ(0, getsockname(sock, reinterpret_cast<sockaddr *>(&sll), &len));
+ EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol);
+ EXPECT_EQ(ifindex, sll.sll_ifindex);
+}
+
TEST_F(ClatdTest, ConfigureIpv6Address) {
- // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk.
- struct tun_data tunnel = {
- .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW),
- .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)),
- };
+ struct tun_data tunnel = makeTunData();
// Run configure_clat_ipv6_address.
ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet));
- ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str()));
+ ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str(), nullptr /* v6_addr */));
// Check that it generated an IID in the same prefix as the address assigned to the interface,
// and that the IID is not the default IID.
@@ -984,6 +1054,26 @@ TEST_F(ClatdTest, ConfigureIpv6Address) {
EXPECT_NE(htonl((uint32_t)0x00000464), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
EXPECT_NE((uint32_t)0, Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+ expectSocketBound(sTun.ifindex(), tunnel.read_fd6);
+
+ freeTunData(&tunnel);
+}
+
+TEST_F(ClatdTest, ConfigureIpv6AddressCommandLine) {
+ struct tun_data tunnel = makeTunData();
+
+ ASSERT_TRUE(IN6_IS_ADDR_UNSPECIFIED(&Global_Clatd_Config.ipv6_local_subnet));
+
+ const char *addrStr = "2001:db8::f00";
+ in6_addr addr;
+ ASSERT_EQ(1, inet_pton(AF_INET6, addrStr, &addr));
+ ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, sTun.name().c_str(), addrStr));
+
+ EXPECT_EQ(htonl(0x20010db8), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[0]);
+ EXPECT_EQ(htonl(0x00000000), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[1]);
+ EXPECT_EQ(htonl(0x00000000), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[2]);
+ EXPECT_EQ(htonl(0x00000f00), Global_Clatd_Config.ipv6_local_subnet.s6_addr32[3]);
+
// Check that the packet socket is bound to the interface. We can't check the socket filter
// because there is no way to fetch it from the kernel.
sockaddr_ll sll;
@@ -991,6 +1081,10 @@ TEST_F(ClatdTest, ConfigureIpv6Address) {
ASSERT_EQ(0, getsockname(tunnel.read_fd6, reinterpret_cast<sockaddr *>(&sll), &len));
EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol);
EXPECT_EQ(sll.sll_ifindex, sTun.ifindex());
+
+ expectSocketBound(sTun.ifindex(), tunnel.read_fd6);
+
+ freeTunData(&tunnel);
}
TEST_F(ClatdTest, Ipv6AddressChanged) {
@@ -1000,7 +1094,7 @@ TEST_F(ClatdTest, Ipv6AddressChanged) {
.read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)),
};
const char *ifname = sTun.name().c_str();
- ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname));
+ ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, nullptr));
EXPECT_EQ(0, ipv6_address_changed(ifname));
EXPECT_EQ(0, ipv6_address_changed(ifname));
diff --git a/main.c b/main.c
index 11c51a5..54d12d1 100644
--- a/main.c
+++ b/main.c
@@ -42,6 +42,8 @@ void print_help() {
printf("android-clat arguments:\n");
printf("-i [uplink interface]\n");
printf("-p [plat prefix]\n");
+ printf("-4 [IPv4 address]\n");
+ printf("-6 [IPv6 address]\n");
printf("-n [NetId]\n");
printf("-m [socket mark]\n");
}
@@ -53,11 +55,12 @@ int main(int argc, char **argv) {
struct tun_data tunnel;
int opt;
char *uplink_interface = NULL, *plat_prefix = NULL, *net_id_str = NULL, *mark_str = NULL;
+ char *v4_addr = NULL, *v6_addr = NULL;
unsigned net_id = NETID_UNSET;
uint32_t mark = MARK_UNSET;
unsigned len;
- while ((opt = getopt(argc, argv, "i:p:n:m:h")) != -1) {
+ while ((opt = getopt(argc, argv, "i:p:4:6:n:m:h")) != -1) {
switch (opt) {
case 'i':
uplink_interface = optarg;
@@ -65,6 +68,12 @@ int main(int argc, char **argv) {
case 'p':
plat_prefix = optarg;
break;
+ case '4':
+ v4_addr = optarg;
+ break;
+ case '6':
+ v6_addr = optarg;
+ break;
case 'n':
net_id_str = optarg;
break;
@@ -101,8 +110,10 @@ int main(int argc, char **argv) {
exit(1);
}
- logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s", CLATD_VERSION,
- uplink_interface, net_id_str ? net_id_str : "(none)", mark_str ? mark_str : "(none)");
+ logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s netid=%s mark=%s plat=%s v4=%s v6=%s",
+ CLATD_VERSION, uplink_interface, net_id_str ? net_id_str : "(none)",
+ mark_str ? mark_str : "(none)", plat_prefix ? plat_prefix : "(none)",
+ v4_addr ? v4_addr : "(none)", v6_addr ? v6_addr : "(none)");
// run under a regular user but keep needed capabilities
drop_root_but_keep_caps();
@@ -125,7 +136,7 @@ int main(int argc, char **argv) {
// following line causes XLAT failure in permissive mode.
unsetenv("ANDROID_DNS_MODE");
- configure_interface(uplink_interface, plat_prefix, &tunnel, net_id);
+ configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, net_id);
// Drop all remaining capabilities.
set_capability(0);