Snap for 10102166 from 7d96bf02a135f6edcdad90c695515a5d5d1ee119 to mainline-tzdata4-release

Change-Id: I42f340ae2840f33601f3e7927d920fb1adc9c6c5
diff --git a/Android.bp b/Android.bp
index 982e656..983ee00 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,7 +40,6 @@
     srcs: [
         "clatd.c",
         "dump.c",
-        "getaddr.c",
         "icmp.c",
         "ipv4.c",
         "ipv6.c",
diff --git a/clatd.c b/clatd.c
index 307211f..9a7a45a 100644
--- a/clatd.c
+++ b/clatd.c
@@ -20,6 +20,7 @@
 #include <fcntl.h>
 #include <poll.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -35,6 +36,7 @@
 #include <linux/if_ether.h>
 #include <linux/if_packet.h>
 #include <linux/if_tun.h>
+#include <linux/virtio_net.h>
 #include <net/if.h>
 #include <sys/uio.h>
 
@@ -42,85 +44,142 @@
 #include "checksum.h"
 #include "config.h"
 #include "dump.h"
-#include "getaddr.h"
 #include "logging.h"
 #include "translate.h"
 
 struct clat_config Global_Clatd_Config;
 
-/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */
-#define MTU_DELTA 28
-
 volatile sig_atomic_t running = 1;
 
-int ipv6_address_changed(const char *interface) {
-  union anyip *interface_ip;
-
-  interface_ip = getinterface_ip(interface, AF_INET6);
-  if (!interface_ip) {
-    logmsg(ANDROID_LOG_ERROR, "Unable to find an IPv6 address on interface %s", interface);
-    return 1;
-  }
-
-  if (!ipv6_prefix_equal(&interface_ip->ip6, &Global_Clatd_Config.ipv6_local_subnet)) {
-    char oldstr[INET6_ADDRSTRLEN];
-    char newstr[INET6_ADDRSTRLEN];
-    inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, oldstr, sizeof(oldstr));
-    inet_ntop(AF_INET6, &interface_ip->ip6, newstr, sizeof(newstr));
-    logmsg(ANDROID_LOG_INFO, "IPv6 prefix on %s changed: %s -> %s", interface, oldstr, newstr);
-    free(interface_ip);
-    return 1;
-  } else {
-    free(interface_ip);
-    return 0;
-  }
-}
-
-/* function: read_packet
- * reads a packet from the tunnel fd and translates it
- *   read_fd  - file descriptor to read original packet from
- *   write_fd - file descriptor to write translated packet to
- *   to_ipv6  - whether the packet is to be translated to ipv6 or ipv4
- */
-void read_packet(int read_fd, int write_fd, int to_ipv6) {
-  uint8_t buf[PACKETLEN];
-  ssize_t readlen = read(read_fd, buf, PACKETLEN);
+// reads IPv6 packet from AF_PACKET socket, translates to IPv4, writes to tun
+void process_packet_6_to_4(struct tun_data *tunnel) {
+  // ethernet header is 14 bytes, plus 4 for a normal VLAN tag or 8 for Q-in-Q
+  // we don't really support vlans (or especially Q-in-Q)...
+  // but a few bytes of extra buffer space doesn't hurt...
+  struct {
+    struct virtio_net_hdr vnet;
+    uint8_t payload[22 + MAXMTU];
+    char pad; // +1 to make packet truncation obvious
+  } buf;
+  struct iovec iov = {
+    .iov_base = &buf,
+    .iov_len = sizeof(buf),
+  };
+  char cmsg_buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
+  struct msghdr msgh = {
+    .msg_iov = &iov,
+    .msg_iovlen = 1,
+    .msg_control = cmsg_buf,
+    .msg_controllen = sizeof(cmsg_buf),
+  };
+  ssize_t readlen = recvmsg(tunnel->read_fd6, &msgh, /*flags*/ 0);
 
   if (readlen < 0) {
     if (errno != EAGAIN) {
-      logmsg(ANDROID_LOG_WARN, "read_packet/read error: %s", strerror(errno));
+      logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno));
     }
     return;
   } else if (readlen == 0) {
-    logmsg(ANDROID_LOG_WARN, "read_packet/tun interface removed");
+    logmsg(ANDROID_LOG_WARN, "%s: packet socket removed?", __func__);
     running = 0;
     return;
-  }
-
-  if (!to_ipv6) {
-    translate_packet(write_fd, 0 /* to_ipv6 */, buf, readlen);
+  } else if (readlen >= sizeof(buf)) {
+    logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__);
     return;
   }
 
-  struct tun_pi *tun_header = (struct tun_pi *)buf;
-  if (readlen < (ssize_t)sizeof(*tun_header)) {
-    logmsg(ANDROID_LOG_WARN, "read_packet/short read: got %ld bytes", readlen);
+  __u32 tp_status = 0;
+  __u16 tp_net = 0;
+
+  for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
+    if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
+      struct tpacket_auxdata *aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg);
+      tp_status = aux->tp_status;
+      tp_net = aux->tp_net;
+      break;
+    }
+  }
+
+  const int payload_offset = offsetof(typeof(buf), payload);
+  if (readlen < payload_offset + tp_net) {
+    logmsg(ANDROID_LOG_WARN, "%s: ignoring %zd byte pkt shorter than %d+%u L2 header",
+           __func__, readlen, payload_offset, tp_net);
     return;
   }
 
-  uint16_t proto = ntohs(tun_header->proto);
+  const int pkt_len = readlen - payload_offset;
+
+  // This will detect a skb->ip_summed == CHECKSUM_PARTIAL packet with non-final L4 checksum
+  if (tp_status & TP_STATUS_CSUMNOTREADY) {
+    static bool logged = false;
+    if (!logged) {
+      logmsg(ANDROID_LOG_WARN, "%s: L4 checksum calculation required", __func__);
+      logged = true;
+    }
+
+    // These are non-negative by virtue of csum_start/offset being u16
+    const int cs_start = buf.vnet.csum_start;
+    const int cs_offset = cs_start + buf.vnet.csum_offset;
+    if (cs_start > pkt_len) {
+      logmsg(ANDROID_LOG_ERROR, "%s: out of range - checksum start %d > %d",
+             __func__, cs_start, pkt_len);
+    } else if (cs_offset + 1 >= pkt_len) {
+      logmsg(ANDROID_LOG_ERROR, "%s: out of range - checksum offset %d + 1 >= %d",
+             __func__, cs_offset, pkt_len);
+    } else {
+      uint16_t csum = ip_checksum(buf.payload + cs_start, pkt_len - cs_start);
+      if (!csum) csum = 0xFFFF;  // required fixup for UDP, TCP must live with it
+      buf.payload[cs_offset] = csum & 0xFF;
+      buf.payload[cs_offset + 1] = csum >> 8;
+    }
+  }
+
+  translate_packet(tunnel->fd4, 0 /* to_ipv6 */, buf.payload + tp_net, pkt_len - tp_net);
+}
+
+// reads TUN_PI + L3 IPv4 packet from tun, translates to IPv6, writes to AF_INET6/RAW socket
+void process_packet_4_to_6(struct tun_data *tunnel) {
+  struct {
+    struct tun_pi pi;
+    uint8_t payload[MAXMTU];
+    char pad; // +1 byte to make packet truncation obvious
+  } buf;
+  ssize_t readlen = read(tunnel->fd4, &buf, sizeof(buf));
+
+  if (readlen < 0) {
+    if (errno != EAGAIN) {
+      logmsg(ANDROID_LOG_WARN, "%s: read error: %s", __func__, strerror(errno));
+    }
+    return;
+  } else if (readlen == 0) {
+    logmsg(ANDROID_LOG_WARN, "%s: tun interface removed", __func__);
+    running = 0;
+    return;
+  } else if (readlen >= sizeof(buf)) {
+    logmsg(ANDROID_LOG_WARN, "%s: read truncation - ignoring pkt", __func__);
+    return;
+  }
+
+  const int payload_offset = offsetof(typeof(buf), payload);
+
+  if (readlen < payload_offset) {
+    logmsg(ANDROID_LOG_WARN, "%s: short read: got %ld bytes", __func__, readlen);
+    return;
+  }
+
+  const int pkt_len = readlen - payload_offset;
+
+  uint16_t proto = ntohs(buf.pi.proto);
   if (proto != ETH_P_IP) {
     logmsg(ANDROID_LOG_WARN, "%s: unknown packet type = 0x%x", __func__, proto);
     return;
   }
 
-  if (tun_header->flags != 0) {
-    logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags);
+  if (buf.pi.flags != 0) {
+    logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, buf.pi.flags);
   }
 
-  uint8_t *packet = (uint8_t *)(tun_header + 1);
-  readlen -= sizeof(*tun_header);
-  translate_packet(write_fd, 1 /* to_ipv6 */, packet, readlen);
+  translate_packet(tunnel->write_fd6, 1 /* to_ipv6 */, buf.payload, pkt_len);
 }
 
 // IPv6 DAD packet format:
@@ -214,36 +273,24 @@
   // TODO: actually perform true DAD
   send_dad(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
 
-  time_t last_interface_poll;
   struct pollfd wait_fd[] = {
     { tunnel->read_fd6, POLLIN, 0 },
     { tunnel->fd4, POLLIN, 0 },
   };
 
-  // start the poll timer
-  last_interface_poll = time(NULL);
-
   while (running) {
-    if (poll(wait_fd, ARRAY_SIZE(wait_fd), NO_TRAFFIC_INTERFACE_POLL_FREQUENCY * 1000) == -1) {
+    if (poll(wait_fd, ARRAY_SIZE(wait_fd), -1) == -1) {
       if (errno != EINTR) {
         logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno));
       }
     } else {
-      // Call read_packet if the socket has data to be read, but also if an
+      // Call process_packet if the socket has data to be read, but also if an
       // error is waiting. If we don't call read() after getting POLLERR, a
       // subsequent poll() will return immediately with POLLERR again,
       // causing this code to spin in a loop. Calling read() will clear the
       // socket error flag instead.
-      if (wait_fd[0].revents) read_packet(tunnel->read_fd6, tunnel->fd4, 0 /* to_ipv6 */);
-      if (wait_fd[1].revents) read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */);
-    }
-
-    time_t now = time(NULL);
-    if (now >= (last_interface_poll + INTERFACE_POLL_FREQUENCY)) {
-      last_interface_poll = now;
-      if (ipv6_address_changed(Global_Clatd_Config.native_ipv6_interface)) {
-        break;
-      }
+      if (wait_fd[0].revents) process_packet_6_to_4(tunnel);
+      if (wait_fd[1].revents) process_packet_4_to_6(tunnel);
     }
   }
 }
diff --git a/clatd.h b/clatd.h
index 87cdbcf..e170c58 100644
--- a/clatd.h
+++ b/clatd.h
@@ -24,21 +24,36 @@
 
 struct tun_data;
 
-#define MAXMTU 65536
-#define PACKETLEN (MAXMTU + sizeof(struct tun_pi))
-#define CLATD_VERSION "1.5"
+// IPv4 header has a u16 total length field, for maximum L3 mtu of 0xFFFF.
+//
+// Translating IPv4 to IPv6 requires removing the IPv4 header (20) and adding
+// an IPv6 header (40), possibly with an extra ipv6 fragment extension header (8).
+//
+// As such the maximum IPv4 L3 mtu size is 0xFFFF (by u16 tot_len field)
+// and the maximum IPv6 L3 mtu size is 0xFFFF + 28 (which is larger)
+//
+// A received non-jumbogram IPv6 frame could potentially be u16 payload_len = 0xFFFF
+// + sizeof ipv6 header = 40, bytes in size.  But such a packet cannot be meaningfully
+// converted to IPv4 (it's too large).  As such the restriction is the same: 0xFFFF + 28
+//
+// (since there's no jumbogram support in IPv4, IPv6 jumbograms cannot be meaningfully
+// converted to IPv4 anyway, and are thus entirely unsupported)
+#define MAXMTU (0xFFFF + 28)
+
+// logcat_hexdump() maximum binary data length, this is the maximum packet size
+// plus some extra space for various headers:
+//   struct tun_pi (4 bytes)
+//   struct virtio_net_hdr (10 bytes)
+//   ethernet (14 bytes), potentially including vlan tag (4) or tags (8 or 12)
+// plus some extra just-in-case headroom, because it doesn't hurt.
+#define MAXDUMPLEN (64 + MAXMTU)
+
+#define CLATD_VERSION "1.7"
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
-// how frequently (in seconds) to poll for an address change while traffic is passing
-#define INTERFACE_POLL_FREQUENCY 30
-
-// how frequently (in seconds) to poll for an address change while there is no traffic
-#define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90
-
 extern volatile sig_atomic_t running;
 
-int ipv6_address_changed(const char *interface);
 void event_loop(struct tun_data *tunnel);
 
 /* function: parse_int
diff --git a/clatd_test.cpp b/clatd_test.cpp
index 8eef738..94ae0b5 100644
--- a/clatd_test.cpp
+++ b/clatd_test.cpp
@@ -33,7 +33,6 @@
 #include "checksum.h"
 #include "clatd.h"
 #include "config.h"
-#include "getaddr.h"
 #include "translate.h"
 }
 
@@ -836,55 +835,3 @@
   check_translate_checksum_neutral(udp_ipv4, sizeof(udp_ipv4), sizeof(udp_ipv4) + 20,
                                    "UDP/IPv4 -> UDP/IPv6 checksum neutral");
 }
-
-TEST_F(ClatdTest, GetInterfaceIpV4) {
-  TunInterface v4Iface;
-  ASSERT_EQ(0, v4Iface.init());
-  EXPECT_EQ(0, v4Iface.addAddress("192.0.2.1", 32));
-
-  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);
-
-  v4Iface.destroy();
-}
-
-TEST_F(ClatdTest, GetInterfaceIpV6) {
-  union anyip *ip = getinterface_ip(sTun.name().c_str(), AF_INET6);
-  ASSERT_NE(nullptr, ip);
-  in6_addr expected = sTun.srcAddr();
-  in6_addr actual   = ip->ip6;
-  expect_ipv6_addr_equal(&expected, &actual);
-}
-
-TEST_F(ClatdTest, Ipv6AddressChanged) {
-  // Configure the clat IPv6 address.
-  const char *ifname = sTun.name().c_str();
-
-  in6_addr myaddr = sTun.srcAddr();
-  gen_random_iid(&myaddr, &Global_Clatd_Config.ipv4_local_subnet, &Global_Clatd_Config.plat_subnet);
-  char addrstr[INET6_ADDRSTRLEN];
-  ASSERT_NE(nullptr, inet_ntop(AF_INET6, &myaddr, addrstr, sizeof(addrstr)));
-
-  Global_Clatd_Config.ipv6_local_subnet = myaddr;
-  EXPECT_EQ(0, ipv6_address_changed(ifname));
-  EXPECT_EQ(0, ipv6_address_changed(ifname));
-
-  // Change the IP address on the tun interface to a new prefix.
-  char srcaddr[INET6_ADDRSTRLEN];
-  char dstaddr[INET6_ADDRSTRLEN];
-  ASSERT_NE(nullptr, inet_ntop(AF_INET6, &sTun.srcAddr(), srcaddr, sizeof(srcaddr)));
-  ASSERT_NE(nullptr, inet_ntop(AF_INET6, &sTun.dstAddr(), dstaddr, sizeof(dstaddr)));
-  EXPECT_EQ(0, ifc_del_address(ifname, srcaddr, 64));
-  EXPECT_EQ(0, ifc_del_address(ifname, dstaddr, 64));
-
-  // Check that we can tell that the address has changed.
-  EXPECT_EQ(0, ifc_add_address(ifname, "2001:db8::1:2", 64));
-  EXPECT_EQ(1, ipv6_address_changed(ifname));
-  EXPECT_EQ(1, ipv6_address_changed(ifname));
-
-  // Restore the tun interface configuration.
-  sTun.destroy();
-  ASSERT_EQ(0, sTun.init());
-}
diff --git a/dump.c b/dump.c
index 289f161..dff3d5e 100644
--- a/dump.c
+++ b/dump.c
@@ -226,11 +226,11 @@
 
 /* generic hex dump */
 void logcat_hexdump(const char *info, const uint8_t *data, size_t len) {
-  char output[PACKETLEN * 3 + 2];
+  char output[MAXDUMPLEN * 3 + 2];
   size_t i;
 
   output[0] = '\0';
-  for (i = 0; i < len && i < PACKETLEN; i++) {
+  for (i = 0; i < len && i < MAXDUMPLEN; i++) {
     snprintf(output + i * 3, 4, " %02x", data[i]);
   }
   output[len * 3 + 3] = '\0';
diff --git a/getaddr.c b/getaddr.c
deleted file mode 100644
index ba8052d..0000000
--- a/getaddr.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright 2012 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.
- *
- * getaddr.c - get a locally configured address
- */
-#include "getaddr.h"
-
-#include <errno.h>
-#include <linux/if_addr.h>
-#include <linux/rtnetlink.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include "logging.h"
-
-// Kernel suggests that keep the packet under 8KiB (NLMSG_GOODSIZE) in include/linux/netlink.h.
-#define NLMSG_SIZE 8192
-
-// shared state between getinterface_ip and parse_ifaddrmsg
-// TODO: refactor the communication between getinterface_ip and parse_ifaddrmsg because there
-// is no netlink callback anymore.
-struct target {
-  int family;
-  unsigned int ifindex;
-  union anyip ip;
-  int foundip;
-};
-
-/* function: parse_ifaddrmsg
- * parse ifaddrmsg for getinterface_ip
- *   nh  - netlink message header
- *   targ_p - (struct target) info for which address we're looking for
- *            and the parsed result if any.
- */
-static void parse_ifaddrmsg(struct nlmsghdr *nh, struct target *targ_p) {
-  struct ifaddrmsg *ifa_p;
-  struct rtattr *rta_p;
-  int rta_len;
-
-  ifa_p = (struct ifaddrmsg *)NLMSG_DATA(nh);
-  rta_p = (struct rtattr *)IFA_RTA(ifa_p);
-
-  if (ifa_p->ifa_index != targ_p->ifindex) return;
-
-  if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return;
-
-  rta_len = IFA_PAYLOAD(nh);
-  for (; RTA_OK(rta_p, rta_len); rta_p = RTA_NEXT(rta_p, rta_len)) {
-    switch (rta_p->rta_type) {
-      case IFA_ADDRESS:
-        if ((targ_p->family == AF_INET6) && !(ifa_p->ifa_flags & IFA_F_SECONDARY)) {
-          memcpy(&targ_p->ip.ip6, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr));
-          targ_p->foundip = 1;
-          return;
-        }
-        break;
-      case IFA_LOCAL:
-        if (targ_p->family == AF_INET) {
-          memcpy(&targ_p->ip.ip4, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr));
-          targ_p->foundip = 1;
-          return;
-        }
-        break;
-    }
-  }
-}
-
-void sendrecv_ifaddrmsg(struct target *targ_p) {
-  int s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
-  if (s < 0) {
-    logmsg(ANDROID_LOG_ERROR, "open NETLINK_ROUTE socket failed %s", strerror(errno));
-    return;
-  }
-
-  // Fill in netlink structures.
-  struct {
-    struct nlmsghdr n;
-    struct ifaddrmsg r;
-  } req = {
-    // Netlink message header.
-    .n.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
-    .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT,
-    .n.nlmsg_type  = RTM_GETADDR,
-
-    // Interface address message header.
-    .r.ifa_family = targ_p->family,
-  };
-
-  // Send interface address message.
-  if ((send(s, &req, req.n.nlmsg_len, 0)) < 0) {
-    logmsg(ANDROID_LOG_ERROR, "send netlink socket failed %s", strerror(errno));
-    close(s);
-    return;
-  }
-
-  // Read interface address message and parse the result if any.
-  ssize_t bytes_read;
-  char buf[NLMSG_SIZE];
-  while ((bytes_read = recv(s, buf, sizeof(buf), 0)) > 0) {
-    struct nlmsghdr *nh = (struct nlmsghdr *)buf;
-    for (; NLMSG_OK(nh, bytes_read); nh = NLMSG_NEXT(nh, bytes_read)) {
-      if (nh->nlmsg_type == NLMSG_DONE) {
-        close(s);
-        return;
-      }
-      if (nh->nlmsg_type == NLMSG_ERROR) {
-        logmsg(ANDROID_LOG_ERROR, "netlink message error");
-        close(s);
-        return;
-      }
-      if (nh->nlmsg_type == RTM_NEWADDR) {
-        // Walk through the all messages and update struct target variable as the deleted
-        // callback behavior of getaddr_cb() which always returns NL_OK.
-        // TODO: review if this can early return once address has been found.
-        parse_ifaddrmsg(nh, targ_p);
-      }
-    }
-  }
-  close(s);
-}
-
-/* function: getinterface_ip
- * finds the first global non-privacy IP of the given family for the given interface, or returns
- * NULL.  caller frees pointer
- *   interface - interface to look for
- *   family    - family
- */
-union anyip *getinterface_ip(const char *interface, int family) {
-  union anyip *retval = NULL;
-  struct target targ  = {
-    .family  = family,
-    .foundip = 0,
-    .ifindex = if_nametoindex(interface),
-  };
-
-  if (targ.ifindex == 0) {
-    return NULL;  // interface not found
-  }
-
-  // sends message and receives the response.
-  sendrecv_ifaddrmsg(&targ);
-
-  if (targ.foundip) {
-    retval = malloc(sizeof(union anyip));
-    if (!retval) {
-      logmsg(ANDROID_LOG_FATAL, "getinterface_ip/out of memory");
-      return NULL;
-    }
-    memcpy(retval, &targ.ip, sizeof(union anyip));
-  }
-
-  return retval;
-}
diff --git a/getaddr.h b/getaddr.h
deleted file mode 100644
index 482ac13..0000000
--- a/getaddr.h
+++ /dev/null
@@ -1,30 +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.
- *
- * getaddr.h - get a locally configured address
- */
-#ifndef __GETADDR_H__
-#define __GETADDR_H__
-
-#include <netinet/in.h>
-
-union anyip {
-  struct in6_addr ip6;
-  struct in_addr ip4;
-};
-
-union anyip *getinterface_ip(const char *interface, int family);
-
-#endif
diff --git a/main.c b/main.c
index 2ab865e..f888041 100644
--- a/main.c
+++ b/main.c
@@ -19,9 +19,12 @@
 #include <arpa/inet.h>
 #include <errno.h>
 #include <netinet/in.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/personality.h>
+#include <sys/utsname.h>
 #include <unistd.h>
 
 #include "clatd.h"
@@ -151,6 +154,34 @@
          uplink_interface, plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)",
          v6_addr ? v6_addr : "(none)");
 
+  {
+    // Compile time detection of 32 vs 64-bit build. (note: C does not have 'constexpr')
+    // Avoid use of preprocessor macros to get compile time syntax checking even on 64-bit.
+    const int user_bits = sizeof(void*) * 8;
+    const bool user32 = (user_bits == 32);
+
+    // Note that on 64-bit all this personality related code simply compile optimizes out.
+    // 32-bit: fetch current personality (see 'man personality': 0xFFFFFFFF means retrieve only)
+    // On Linux fetching personality cannot fail.
+    const int prev_personality = user32 ? personality(0xFFFFFFFFuL) : PER_LINUX;
+    // 32-bit: attempt to get rid of kernel spoofing of 'uts.machine' architecture,
+    // In theory this cannot fail, as PER_LINUX should always be supported.
+    if (user32) (void)personality((prev_personality & ~PER_MASK) | PER_LINUX);
+    // 64-bit: this will compile time evaluate to false.
+    const bool was_linux32 = (prev_personality & PER_MASK) == PER_LINUX32;
+
+    struct utsname uts = {};
+    if (uname(&uts)) exit(1); // only possible error is EFAULT, but 'uts' is on stack
+
+    // sysname is likely 'Linux', release is 'kver', machine is kernel's *true* architecture
+    logmsg(ANDROID_LOG_INFO, "%d-bit userspace on %s kernel %s for %s%s.", user_bits,
+           uts.sysname, uts.release, uts.machine, was_linux32 ? " (was spoofed)" : "");
+
+    // 32-bit: try to return to the 'default' personality
+    // In theory this cannot fail, because it was already previously in use.
+    if (user32) (void)personality(prev_personality);
+  }
+
   // Loop until someone sends us a signal or brings down the tun interface.
   if (signal(SIGTERM, stop_loop) == SIG_ERR) {
     logmsg(ANDROID_LOG_FATAL, "sigterm handler failed: %s", strerror(errno));
@@ -163,8 +194,12 @@
 
   if (running) {
     logmsg(ANDROID_LOG_INFO, "Clatd on %s waiting for SIGTERM", uplink_interface);
-    while (running) sleep(60);
-    logmsg(ANDROID_LOG_INFO, "Clatd on %s received SIGTERM", uplink_interface);
+    // let's give higher level java code 15 seconds to kill us,
+    // but eventually terminate anyway, in case system server forgets about us...
+    // sleep() should be interrupted by SIGTERM, the handler should clear running
+    sleep(15);
+    logmsg(ANDROID_LOG_INFO, "Clatd on %s %s SIGTERM", uplink_interface,
+           running ? "timed out waiting for" : "received");
   } else {
     logmsg(ANDROID_LOG_INFO, "Clatd on %s already received SIGTERM", uplink_interface);
   }