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);
}