DO NOT MERGE - Merge pi-dev@5234907 into stage-aosp-master

Bug: 120848293
Change-Id: Ibbd16bc77a559cb6af5c4a00c5c51f623ac5b710
diff --git a/.clang-format b/.clang-format
index 6682f77..f1debbd 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,6 +1,3 @@
-# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
-# Please keep this file in sync with system/core/.clang-format-4
-#
 BasedOnStyle: Google
 AlignConsecutiveAssignments: true
 AlignEscapedNewlines: Right
diff --git a/Android.bp b/Android.bp
index 82295f3..e0eeb2e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -21,7 +21,6 @@
 filegroup {
     name: "clatd_common",
     srcs: [
-        "checksum.c",
         "config.c",
         "clatd.c",
         "dns64.c",
@@ -90,7 +89,9 @@
     defaults: ["clatd_defaults"],
     srcs: [
         "clatd_microbenchmark.c",
-        "checksum.c",
         "tun.c",
     ],
+    shared_libs: [
+        "libnetutils",
+    ],
 }
diff --git a/OWNERS b/OWNERS
index 6c1758b..eb9ff18 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1 @@
 include platform/system/netd:/OWNERS
-
-# TODO: remove this after we make sure first line is working.
-# Add one owner as fail-safe.
-lorenzo@google.com
-
diff --git a/checksum.c b/checksum.c
deleted file mode 100644
index 11b5149..0000000
--- a/checksum.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2011 Daniel Drown
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * checksum.c - ipv4/ipv6 checksum calculation
- */
-#include <netinet/icmp6.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <netinet/ip_icmp.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
-
-#include "checksum.h"
-
-/* function: ip_checksum_add
- * adds data to a checksum
- * current - the current checksum (or 0 to start a new checksum)
- *   data        - the data to add to the checksum
- *   len         - length of data
- */
-uint32_t ip_checksum_add(uint32_t current, const void *data, int len) {
-  uint32_t checksum       = current;
-  int left                = len;
-  const uint16_t *data_16 = data;
-
-  while (left > 1) {
-    checksum += *data_16;
-    data_16++;
-    left -= 2;
-  }
-  if (left) {
-    checksum += *(uint8_t *)data_16;
-  }
-
-  return checksum;
-}
-
-/* function: ip_checksum_fold
- * folds a 32-bit partial checksum into 16 bits
- *   temp_sum - sum from ip_checksum_add
- *   returns: the folded checksum in network byte order
- */
-uint16_t ip_checksum_fold(uint32_t temp_sum) {
-  while (temp_sum > 0xffff) {
-    temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
-  }
-  return temp_sum;
-}
-
-/* function: ip_checksum_finish
- * folds and closes the checksum
- *   temp_sum - sum from ip_checksum_add
- *   returns: a header checksum value in network byte order
- */
-uint16_t ip_checksum_finish(uint32_t temp_sum) { return ~ip_checksum_fold(temp_sum); }
-
-/* function: ip_checksum
- * combined ip_checksum_add and ip_checksum_finish
- *   data - data to checksum
- *   len  - length of data
- */
-uint16_t ip_checksum(const void *data, int len) {
-  uint32_t temp_sum;
-
-  temp_sum = ip_checksum_add(0, data, len);
-  return ip_checksum_finish(temp_sum);
-}
-
-/* function: ipv6_pseudo_header_checksum
- * calculate the pseudo header checksum for use in tcp/udp/icmp headers
- *   ip6      - the ipv6 header
- *   len      - the transport length (transport header + payload)
- *   protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments
- */
-uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) {
-  uint32_t checksum_len, checksum_next;
-  checksum_len  = htonl((uint32_t)len);
-  checksum_next = htonl(protocol);
-
-  uint32_t current = 0;
-
-  current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
-  current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
-  current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
-  current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next));
-
-  return current;
-}
-
-/* function: ipv4_pseudo_header_checksum
- * calculate the pseudo header checksum for use in tcp/udp headers
- *   ip      - the ipv4 header
- *   len     - the transport length (transport header + payload)
- */
-uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) {
-  uint16_t temp_protocol, temp_length;
-
-  temp_protocol = htons(ip->protocol);
-  temp_length   = htons(len);
-
-  uint32_t current = 0;
-
-  current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
-  current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
-  current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
-  current = ip_checksum_add(current, &temp_length, sizeof(uint16_t));
-
-  return current;
-}
-
-/* function: ip_checksum_adjust
- * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
- *   checksum    - the header checksum in the original packet in network byte order
- *   old_hdr_sum - the pseudo-header checksum of the original packet
- *   new_hdr_sum - the pseudo-header checksum of the translated packet
- *   returns: the new header checksum in network byte order
- */
-uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
-  // Algorithm suggested in RFC 1624.
-  // http://tools.ietf.org/html/rfc1624#section-3
-  checksum            = ~checksum;
-  uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
-  uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
-  if (folded_sum > folded_old) {
-    return ~(folded_sum - folded_old);
-  } else {
-    return ~(folded_sum - folded_old - 1);  // end-around borrow
-  }
-}
diff --git a/checksum.h b/checksum.h
deleted file mode 100644
index 669b42c..0000000
--- a/checksum.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2011 Daniel Drown
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * checksum.h - checksum functions
- */
-#ifndef __CHECKSUM_H__
-#define __CHECKSUM_H__
-
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <stdint.h>
-
-uint32_t ip_checksum_add(uint32_t current, const void *data, int len);
-uint16_t ip_checksum_finish(uint32_t temp_sum);
-uint16_t ip_checksum(const void *data, int len);
-
-uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol);
-uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len);
-
-uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
-
-#endif /* __CHECKSUM_H__ */
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_microbenchmark.c b/clatd_microbenchmark.c
index 83be006..91b0996 100644
--- a/clatd_microbenchmark.c
+++ b/clatd_microbenchmark.c
@@ -41,7 +41,7 @@
 #include <time.h>
 #include <unistd.h>
 
-#include "checksum.h"
+#include "netutils/checksum.h"
 #include "tun.h"
 
 #define DEVICENAME "clat4"
diff --git a/clatd_test.cpp b/clatd_test.cpp
index 81af41d..8e49824 100644
--- a/clatd_test.cpp
+++ b/clatd_test.cpp
@@ -29,10 +29,10 @@
 #include "tun_interface.h"
 
 extern "C" {
-#include "checksum.h"
 #include "clatd.h"
 #include "config.h"
 #include "getaddr.h"
+#include "netutils/checksum.h"
 #include "translate.h"
 #include "tun.h"
 }
@@ -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/config.c b/config.c
index 91a65fb..5a9d599 100644
--- a/config.c
+++ b/config.c
@@ -25,9 +25,9 @@
 #include <unistd.h>
 
 #include <cutils/config_utils.h>
+#include <netutils/checksum.h>
 #include <netutils/ifc.h>
 
-#include "checksum.h"
 #include "clatd.h"
 #include "config.h"
 #include "dns64.h"
diff --git a/dump.c b/dump.c
index 2c948ff..28db13f 100644
--- a/dump.c
+++ b/dump.c
@@ -29,7 +29,8 @@
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
 
-#include "checksum.h"
+#include "netutils/checksum.h"
+
 #include "clatd.h"
 #include "debug.h"
 #include "logging.h"
diff --git a/ipv4.c b/ipv4.c
index fce400f..56d83ac 100644
--- a/ipv4.c
+++ b/ipv4.c
@@ -17,7 +17,8 @@
  */
 #include <string.h>
 
-#include "checksum.h"
+#include "netutils/checksum.h"
+
 #include "debug.h"
 #include "dump.h"
 #include "logging.h"
diff --git a/ipv6.c b/ipv6.c
index a11dd1a..c9b547e 100644
--- a/ipv6.c
+++ b/ipv6.c
@@ -19,7 +19,8 @@
 
 #include <arpa/inet.h>
 
-#include "checksum.h"
+#include "netutils/checksum.h"
+
 #include "config.h"
 #include "debug.h"
 #include "dump.h"
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);
diff --git a/translate.c b/translate.c
index df3d020..1632753 100644
--- a/translate.c
+++ b/translate.c
@@ -17,7 +17,8 @@
  */
 #include <string.h>
 
-#include "checksum.h"
+#include "netutils/checksum.h"
+
 #include "clatd.h"
 #include "common.h"
 #include "config.h"