From ce14088b80c2d22add83a616e567f9025d252ef7 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 2 Jun 2014 21:20:40 +0900 Subject: Use a raw socket to send IPv6 packets instead of a tun. This will allow us to bind the socket to a particular network. (cherry picked from commit 10e8827d636a72a7bcdfd52d15bad9342ae2a0a6) Bug: 15340961 Change-Id: I0b62ef96364a90b9c0a9e3ac3ba97b5c19c89b69 --- clatd.c | 30 +++++++++++++++++++++++++----- clatd.h | 2 +- clatd_test.cpp | 37 +++++++++++++++++++++++++------------ translate.c | 39 +++++++++++++++++++++++++++++++++------ 4 files changed, 84 insertions(+), 24 deletions(-) diff --git a/clatd.c b/clatd.c index af54ff2..be7d0c6 100644 --- a/clatd.c +++ b/clatd.c @@ -248,6 +248,24 @@ void drop_root() { } } +/* 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 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, 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 @@ void read_packet(int active_fd, const struct tun_data *tunnel) { 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 @@ void event_loop(const struct tun_data *tunnel) { // 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 @@ int main(int argc, char **argv) { 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 @@ int main(int argc, char **argv) { 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 @@ void fix_udp_checksum(uint8_t* packet) { 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 @@ void do_translate_packet(const uint8_t *original, size_t original_len, uint8_t * 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 @@ int tcp_translate(clat_packet out, clat_packet_index pos, const struct tcphdr *t 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 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets // 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 @@ void translate_packet(int fd, int to_ipv6, const uint8_t *packet, size_t packets 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); + } } } -- cgit v1.2.3