Use a raw socket to send IPv6 packets instead of a tun.

This will allow us to bind the socket to a particular network.

Bug: 15340961
Change-Id: I50857d372955f2b6f7035157c2968cda72c32585
diff --git a/clatd.c b/clatd.c
index af54ff2..be7d0c6 100644
--- a/clatd.c
+++ b/clatd.c
@@ -248,6 +248,24 @@
   }
 }
 
+/* function: open_raw_socket
+ * opens the raw socket for sending IPv6 packets
+ */
+void open_raw_socket(struct tun_data *tunnel) {
+  int rawsock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
+  if (rawsock < 0) {
+    logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno));
+    exit(1);
+  }
+
+  int off = 0;
+  if (setsockopt(rawsock, SOL_IPV6, IPV6_CHECKSUM, &off, sizeof(off)) < 0) {
+    logmsg(ANDROID_LOG_WARN, "could not disable checksum on raw socket: %s", strerror(errno));
+  }
+
+  tunnel->write_fd6 = rawsock;
+}
+
 /* function: configure_interface
  * reads the configuration and applies it to the interface
  * uplink_interface - network interface to use to reach the ipv6 internet
@@ -282,7 +300,7 @@
     logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu);
   }
 
-  error = tun_alloc(tunnel->device6, tunnel->fd6);
+  error = tun_alloc(tunnel->device6, tunnel->read_fd6);
   if(error < 0) {
     logmsg(ANDROID_LOG_FATAL,"tun_alloc failed: %s",strerror(errno));
     exit(1);
@@ -334,7 +352,7 @@
     int fd;
     uint16_t proto = ntohs(tun_header->proto);
     if (proto == ETH_P_IP) {
-      fd = tunnel->fd6;
+      fd = tunnel->write_fd6;
     } else if (proto == ETH_P_IPV6) {
       fd = tunnel->fd4;
     } else {
@@ -357,7 +375,7 @@
   // start the poll timer
   last_interface_poll = time(NULL);
 
-  wait_fd[0].fd = tunnel->fd6;
+  wait_fd[0].fd = tunnel->read_fd6;
   wait_fd[0].events = POLLIN;
   wait_fd[0].revents = 0;
   wait_fd[1].fd = tunnel->fd4;
@@ -443,8 +461,8 @@
   logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface);
 
   // open the tunnel device before dropping privs
-  tunnel.fd6 = tun_open();
-  if(tunnel.fd6 < 0) {
+  tunnel.read_fd6 = tun_open();
+  if(tunnel.read_fd6 < 0) {
     logmsg(ANDROID_LOG_FATAL, "tun_open6 failed: %s", strerror(errno));
     exit(1);
   }
@@ -463,6 +481,8 @@
     exit(1);
   }
 
+  open_raw_socket(&tunnel);
+
   // run under a regular user
   drop_root();
 
diff --git a/clatd.h b/clatd.h
index 29c9ace..0a48013 100644
--- a/clatd.h
+++ b/clatd.h
@@ -33,7 +33,7 @@
 
 struct tun_data {
   char device6[IFNAMSIZ], device4[IFNAMSIZ];
-  int fd6, fd4;
+  int read_fd6, write_fd6, fd4;
 };
 
 #endif /* __CLATD_H__ */
diff --git a/clatd_test.cpp b/clatd_test.cpp
index bc32a84..b35bf70 100644
--- a/clatd_test.cpp
+++ b/clatd_test.cpp
@@ -418,6 +418,13 @@
   udp->check = ip_checksum_finish(ip_checksum_add(pseudo_checksum, udp, ntohs(udp->len)));
 }
 
+// Testing stub for send_rawv6. The real version uses sendmsg() with a
+// destination IPv6 address, and attempting to call that on our test socketpair
+// fd results in EINVAL.
+extern "C" void send_rawv6(int fd, clat_packet out, int iov_len) {
+    writev(fd, out, iov_len);
+}
+
 void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t *out, size_t *outlen,
                          const char *msg) {
   int fds[2];
@@ -453,19 +460,25 @@
 
   translate_packet(write_fd, (version == 4), original, original_len);
 
-  struct tun_pi new_tun_header;
-  struct iovec iov[] = {
-    { &new_tun_header, sizeof(new_tun_header) },
-    { out, *outlen }
-  };
-  int len = readv(read_fd, iov, 2);
-  if (len > (int) sizeof(new_tun_header)) {
-    ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n";
-    EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n";
-    *outlen = len - sizeof(new_tun_header);
+  if (version == 6) {
+    // Translating to IPv4. Expect a tun header.
+    struct tun_pi new_tun_header;
+    struct iovec iov[] = {
+      { &new_tun_header, sizeof(new_tun_header) },
+      { out, *outlen }
+    };
+    int len = readv(read_fd, iov, 2);
+    if (len > (int) sizeof(new_tun_header)) {
+      ASSERT_LT((size_t) len, *outlen) << msg << ": Translated packet buffer too small\n";
+      EXPECT_EQ(expected_proto, new_tun_header.proto) << msg << "Unexpected tun proto\n";
+      *outlen = len - sizeof(new_tun_header);
+    } else {
+      FAIL() << msg << ": Packet was not translated";
+      *outlen = 0;
+    }
   } else {
-    FAIL() << msg << ": Packet was not translated";
-    *outlen = 0;
+    // Translating to IPv6. Expect raw packet.
+    *outlen = read(read_fd, out, *outlen);
   }
 }
 
diff --git a/translate.c b/translate.c
index 958ce2f..487468b 100644
--- a/translate.c
+++ b/translate.c
@@ -465,6 +465,30 @@
   return CLAT_POS_PAYLOAD + 1;
 }
 
+void send_tun(int fd, clat_packet out, int iov_len) {
+  writev(fd, out, iov_len);
+}
+
+// Weak symbol so we can override it in the unit test.
+void send_rawv6(int fd, clat_packet out, int iov_len) __attribute__((weak));
+
+void send_rawv6(int fd, clat_packet out, int iov_len) {
+  // A send on a raw socket requires a destination address to be specified even if the socket's
+  // protocol is IPPROTO_RAW. This is the address that will be used in routing lookups; the
+  // destination address in the packet header only affects what appears on the wire, not where the
+  // packet is sent to.
+  static struct sockaddr_in6 sin6 = { AF_INET6, 0, 0, { { { 0, 0, 0, 0 } } }, 0 };
+  static struct msghdr msg = {
+    .msg_name = &sin6,
+    .msg_namelen = sizeof(sin6),
+  };
+
+  msg.msg_iov = out,
+  msg.msg_iovlen = iov_len,
+  sin6.sin6_addr = ((struct ip6_hdr *) out[CLAT_POS_IPHDR].iov_base)->ip6_dst;
+  sendmsg(fd, &msg, 0);
+}
+
 /* function: translate_packet
  * takes a packet, translates it, and writes it to fd
  * fd         - fd to write translated packet to
@@ -486,7 +510,7 @@
 
   // iovec of the packets we'll send. This gets passed down to the translation functions.
   clat_packet out = {
-    { &tun_targ, sizeof(tun_targ) },  // Tunnel header.
+    { &tun_targ, 0 },                 // Tunnel header.
     { iphdr, 0 },                     // IP header.
     { fraghdr, 0 },                   // Fragment header.
     { transporthdr, 0 },              // Transport layer header.
@@ -498,12 +522,15 @@
 
   if (to_ipv6) {
     iov_len = ipv4_packet(out, CLAT_POS_IPHDR, packet, packetsize);
+    if (iov_len > 0) {
+      send_rawv6(fd, out, iov_len);
+    }
   } else {
     iov_len = ipv6_packet(out, CLAT_POS_IPHDR, packet, packetsize);
-  }
-
-  if (iov_len > 0) {
-    fill_tun_header(&tun_targ, to_ipv6 ? ETH_P_IPV6 : ETH_P_IP);
-    writev(fd, out, iov_len);
+    if (iov_len > 0) {
+      fill_tun_header(&tun_targ, ETH_P_IP);
+      out[CLAT_POS_TUNHDR].iov_len = sizeof(tun_targ);
+      send_tun(fd, out, iov_len);
+    }
   }
 }