Merge "clatd's .clang-format shouldn't be kept in sync with system/core"
diff --git a/clatd.c b/clatd.c
index bcbd9c5..3ccceea 100644
--- a/clatd.c
+++ b/clatd.c
@@ -110,13 +110,11 @@
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 @@
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 @@
}
}
-/* 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 @@
// 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 @@
* 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 @@
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 @@
#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 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 @@
}
}
+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 @@
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 @@
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 @@
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 @@
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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
// 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);