diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-01-19 08:06:20 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-01-19 08:06:20 +0000 |
commit | 231b290b6a9bc7a8aa14aa1d3c5cb33ac8e25242 (patch) | |
tree | ade7edd26505767dbc5ea08fcf5156077ef1e890 | |
parent | 37fd97989ee9d7d687649549e29bd8acd5793577 (diff) | |
parent | b8c1bb3c6dcb03eab62dfa5719c73cbada7d60a8 (diff) | |
download | android-clat-android-mainline-12.0.0_r83.tar.gz |
Snap for 8093028 from b8c1bb3c6dcb03eab62dfa5719c73cbada7d60a8 to mainline-wifi-releaseandroid-mainline-12.0.0_r83
Change-Id: Iff284fa38bf8b97b50ea8c58340e3c33022f8101
-rw-r--r-- | Android.bp | 36 | ||||
-rw-r--r-- | clatd.c | 258 | ||||
-rw-r--r-- | clatd.h | 13 | ||||
-rw-r--r-- | clatd_test.cpp | 100 | ||||
-rw-r--r-- | config.h | 3 | ||||
-rw-r--r-- | dump.c | 11 | ||||
-rw-r--r-- | getaddr.c | 139 | ||||
-rw-r--r-- | getaddr.h | 2 | ||||
-rw-r--r-- | ipv4.c | 3 | ||||
-rw-r--r-- | ipv6.c | 6 | ||||
-rw-r--r-- | main.c | 83 | ||||
-rw-r--r-- | netlink_callbacks.c | 67 | ||||
-rw-r--r-- | netlink_callbacks.h | 24 | ||||
-rw-r--r-- | netlink_msg.c | 182 | ||||
-rw-r--r-- | netlink_msg.h | 30 | ||||
-rw-r--r-- | ring.c | 128 | ||||
-rw-r--r-- | ring.h | 55 | ||||
-rw-r--r-- | setif.c | 184 | ||||
-rw-r--r-- | setif.h | 28 | ||||
-rw-r--r-- | translate.c | 6 | ||||
-rw-r--r-- | vendor-464xlat.rc | 15 |
21 files changed, 202 insertions, 1171 deletions
@@ -32,11 +32,6 @@ cc_defaults { // Bug: http://b/33566695 "-Wno-address-of-packed-member", ], - - // For MARK_UNSET. - header_libs: [ - "libnetd_client_headers" - ], } // Code used both by the daemon and by unit tests. @@ -50,10 +45,6 @@ filegroup { "ipv4.c", "ipv6.c", "logging.c", - "netlink_callbacks.c", - "netlink_msg.c", - "ring.c", - "setif.c", "translate.c", ], } @@ -62,16 +53,25 @@ filegroup { cc_binary { name: "clatd", defaults: ["clatd_defaults"], + // TODO: remove once drop_root_and_caps() is removed. + header_libs: [ + "libcutils_headers", // for AID_CLAT + ], srcs: [ ":clatd_common", "main.c" ], - static_libs: ["libnl"], + static_libs: [ + "libip_checksum", + ], shared_libs: [ - "libcutils", "liblog", - "libnetutils", ], + relative_install_path: "for-system", + + // Static libc++ for smaller apex size while shipping clatd in the mainline module. + // See b/213123047 + stl: "libc++_static", // Only enable clang-tidy for the daemon, not the tests, because enabling it for the // tests substantially increases build/compile cycle times and doesn't really provide a @@ -86,13 +86,15 @@ cc_binary { tidy_flags: [ "-warnings-as-errors=clang-analyzer-security*,cert-*,android-*", ], - // Actually not required for clatd itself. See comments in the rc file. - init_rc: [ - "vendor-464xlat.rc", - ], sanitize: { memtag_heap: true, }, + + apex_available: [ + "com.android.tethering", + "//apex_available:platform", + ], + min_sdk_version: "30", } // Unit tests. @@ -105,8 +107,8 @@ cc_test { ], static_libs: [ "libbase", + "libip_checksum", "libnetd_test_tun_interface", - "libnl", ], shared_libs: [ "libcutils", @@ -39,7 +39,6 @@ #include <sys/capability.h> #include <sys/uio.h> -#include <netid_client.h> // For MARK_UNSET. #include <private/android_filesystem_config.h> // For AID_CLAT. #include "clatd.h" @@ -47,8 +46,6 @@ #include "dump.h" #include "getaddr.h" #include "logging.h" -#include "ring.h" -#include "setif.h" #include "translate.h" struct clat_config Global_Clatd_Config; @@ -58,84 +55,6 @@ struct clat_config Global_Clatd_Config; volatile sig_atomic_t running = 1; -/* function: configure_packet_socket - * Binds the packet socket and attaches the receive filter to it. - * sock - the socket to configure - */ -int configure_packet_socket(int sock) { - uint32_t *ipv6 = Global_Clatd_Config.ipv6_local_subnet.s6_addr32; - - // clang-format off - struct sock_filter filter_code[] = { - // Load the first four bytes of the IPv6 destination address (starts 24 bytes in). - // Compare it against the first four bytes of our IPv6 address, in host byte order (BPF loads - // are always in host byte order). If it matches, continue with next instruction (JMP 0). If it - // doesn't match, jump ahead to statement that returns 0 (ignore packet). Repeat for the other - // three words of the IPv6 address, and if they all match, return PACKETLEN (accept packet). - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 24), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[0]), 0, 7), - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 28), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[1]), 0, 5), - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 32), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[2]), 0, 3), - BPF_STMT(BPF_LD | BPF_W | BPF_ABS, 36), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(ipv6[3]), 0, 1), - BPF_STMT(BPF_RET | BPF_K, PACKETLEN), - BPF_STMT(BPF_RET | BPF_K, 0), - }; - // clang-format on - struct sock_fprog filter = { sizeof(filter_code) / sizeof(filter_code[0]), filter_code }; - - if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) { - logmsg(ANDROID_LOG_FATAL, "attach packet filter failed: %s", strerror(errno)); - return 0; - } - - struct sockaddr_ll sll = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_IPV6), - .sll_ifindex = if_nametoindex(Global_Clatd_Config.native_ipv6_interface), - .sll_pkttype = PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. - }; - if (bind(sock, (struct sockaddr *)&sll, sizeof(sll))) { - logmsg(ANDROID_LOG_FATAL, "binding packet socket: %s", strerror(errno)); - return 0; - } - - return 1; -} - -/* function: configure_tun_ip - * configures the ipv4 and ipv6 addresses on the tunnel interface - * tunnel - tun device data - * mtu - mtu of tun device - */ -void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu) { - if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) { - logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); - exit(1); - } - - 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); - - // 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, mtu); - if (status < 0) { - logmsg(ANDROID_LOG_FATAL, "configure_tun_ip/if_up(4) failed: %s", strerror(-status)); - exit(1); - } -} - /* function: set_capability * set the permitted, effective and inheritable capabilities of the current * thread @@ -156,18 +75,16 @@ void set_capability(uint64_t target_cap) { } } -/* function: drop_root_but_keep_caps - * drops root privs but keeps the needed capabilities +/* function: drop_root_and_caps + * drops root privs and all capabilities */ -void drop_root_but_keep_caps() { - gid_t groups[] = { AID_INET, AID_VPN }; - if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) < 0) { +void drop_root_and_caps() { + // see man setgroups: this drops all supplementary groups + if (setgroups(0, NULL) < 0) { logmsg(ANDROID_LOG_FATAL, "setgroups failed: %s", strerror(errno)); exit(1); } - prctl(PR_SET_KEEPCAPS, 1); - if (setresgid(AID_CLAT, AID_CLAT, AID_CLAT) < 0) { logmsg(ANDROID_LOG_FATAL, "setresgid failed: %s", strerror(errno)); exit(1); @@ -177,35 +94,7 @@ void drop_root_but_keep_caps() { exit(1); } - // keep CAP_NET_RAW capability to open raw socket, and CAP_IPC_LOCK for mmap - // to lock memory. - set_capability((1 << CAP_NET_ADMIN) | - (1 << CAP_NET_RAW) | - (1 << CAP_IPC_LOCK)); -} - -/* function: open_sockets - * opens a packet socket to receive IPv6 packets and a raw socket to send them - * tunnel - tun device data - * mark - the socket mark to use for the sending raw socket - */ -void open_sockets(struct tun_data *tunnel, uint32_t mark) { - int rawsock = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_RAW); - if (rawsock < 0) { - logmsg(ANDROID_LOG_FATAL, "raw socket failed: %s", strerror(errno)); - exit(1); - } - - if (mark != MARK_UNSET && setsockopt(rawsock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { - logmsg(ANDROID_LOG_ERROR, "could not set mark on raw socket: %s", strerror(errno)); - } - - tunnel->write_fd6 = rawsock; - - tunnel->read_fd6 = ring_create(tunnel); - if (tunnel->read_fd6 < 0) { - exit(1); - } + set_capability(0); } int ipv6_address_changed(const char *interface) { @@ -231,111 +120,6 @@ int ipv6_address_changed(const char *interface) { } } -/* 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) { - if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { - logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); - 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. - add_anycast_address(tunnel->write_fd6, &Global_Clatd_Config.ipv6_local_subnet, interface); - - // Update our packet socket filter to reflect the new 464xlat IP address. - if (!configure_packet_socket(tunnel->read_fd6)) { - // Things aren't going to work. Bail out and hope we have better luck next time. - // We don't log an error here because configure_packet_socket has already done so. - return 0; - } - - return 1; -} - -int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark) { - // Create an IPv6 UDP socket. - int s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (s < 0) { - logmsg(ANDROID_LOG_FATAL, "socket(AF_INET6, SOCK_DGRAM, 0) failed"); - exit(1); - } - - // Socket's mark affects routing decisions (network selection) - if ((mark != MARK_UNSET) && setsockopt(s, SOL_SOCKET, SO_MARK, &mark, sizeof(mark))) { - logmsg(ANDROID_LOG_FATAL, "setsockopt(SOL_SOCKET, SO_MARK) failed: %s", strerror(errno)); - exit(1); - } - - // Try to connect udp socket to plat_subnet(96 bits):plat_suffix(32 bits) - struct sockaddr_in6 dst = { - .sin6_family = AF_INET6, - .sin6_addr = *plat_subnet, - }; - dst.sin6_addr.s6_addr32[3] = plat_suffix; - if (connect(s, (struct sockaddr *)&dst, sizeof(dst))) { - logmsg(ANDROID_LOG_FATAL, "connect() failed: %s", strerror(errno)); - exit(1); - } - - // Fetch the socket's IPv6 mtu - this is effectively fetching mtu from routing table - int mtu; - socklen_t sz_mtu = sizeof(mtu); - if (getsockopt(s, SOL_IPV6, IPV6_MTU, &mtu, &sz_mtu)) { - logmsg(ANDROID_LOG_FATAL, "getsockopt(SOL_IPV6, IPV6_MTU) failed: %s", strerror(errno)); - exit(1); - } - if (sz_mtu != sizeof(mtu)) { - logmsg(ANDROID_LOG_FATAL, "getsockopt(SOL_IPV6, IPV6_MTU) returned unexpected size: %d", - sz_mtu); - exit(1); - } - close(s); - - return mtu; -} - -/* function: configure_interface - * reads the configuration and applies it to the interface - * uplink_interface - network interface to use to reach the ipv6 internet - * plat_prefix - PLAT prefix to use - * v4_addr - the v4 address to use on the tunnel interface - * v6_addr - the v6 address to use on the native interface - * tunnel - tun device data - * mark - the socket mark to use for the sending raw socket - */ -void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6_addr, struct tun_data *tunnel, uint32_t mark) { - Global_Clatd_Config.native_ipv6_interface = uplink_interface; - if (!plat_prefix || inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) { - logmsg(ANDROID_LOG_FATAL, "invalid IPv6 address specified for plat prefix: %s", plat_prefix); - exit(1); - } - - int mtu = detect_mtu(&Global_Clatd_Config.plat_subnet, htonl(0x08080808), mark); - // clamp to minimum ipv6 mtu - this probably cannot ever trigger - if (mtu < 1280) mtu = 1280; - // clamp to buffer size - if (mtu > MAXMTU) mtu = MAXMTU; - // decrease by ipv6(40) + ipv6 fragmentation header(8) vs ipv4(20) overhead of 28 bytes - mtu -= MTU_DELTA; - logmsg(ANDROID_LOG_WARN, "ipv4 mtu is %d", mtu); - - configure_tun_ip(tunnel, v4_addr, mtu); - - if (!configure_clat_ipv6_address(tunnel, uplink_interface, v6_addr)) { - exit(1); - } -} - /* function: read_packet * reads a packet from the tunnel fd and translates it * read_fd - file descriptor to read original packet from @@ -343,10 +127,8 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix, * 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) { - ssize_t readlen; - uint8_t buf[PACKETLEN], *packet; - - readlen = read(read_fd, buf, PACKETLEN); + uint8_t buf[PACKETLEN]; + ssize_t readlen = read(read_fd, buf, PACKETLEN); if (readlen < 0) { if (errno != EAGAIN) { @@ -359,6 +141,11 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { return; } + if (!to_ipv6) { + translate_packet(write_fd, 0 /* to_ipv6 */, buf, readlen); + 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); @@ -375,9 +162,9 @@ void read_packet(int read_fd, int write_fd, int to_ipv6) { logmsg(ANDROID_LOG_WARN, "%s: unexpected flags = %d", __func__, tun_header->flags); } - packet = (uint8_t *)(tun_header + 1); + uint8_t *packet = (uint8_t *)(tun_header + 1); readlen -= sizeof(*tun_header); - translate_packet(write_fd, to_ipv6, packet, readlen); + translate_packet(write_fd, 1 /* to_ipv6 */, packet, readlen); } /* function: event_loop @@ -400,24 +187,13 @@ void event_loop(struct tun_data *tunnel) { logmsg(ANDROID_LOG_WARN, "event_loop/poll returned an error: %s", strerror(errno)); } } else { - if (wait_fd[0].revents & POLLIN) { - ring_read(&tunnel->ring, tunnel->fd4, 0 /* to_ipv6 */); - } - // If any other bit is set, assume it's due to an error (i.e. POLLERR). - if (wait_fd[0].revents & ~POLLIN) { - // ring_read doesn't clear the error indication on the socket. - recv(tunnel->read_fd6, NULL, 0, MSG_PEEK); - logmsg(ANDROID_LOG_WARN, "event_loop: clearing error on read_fd6: %s", strerror(errno)); - } - // Call read_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[1].revents) { - read_packet(tunnel->fd4, tunnel->write_fd6, 1 /* to_ipv6 */); - } + 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); @@ -24,9 +24,9 @@ struct tun_data; -#define MAXMTU 1500 +#define MAXMTU 65536 #define PACKETLEN (MAXMTU + sizeof(struct tun_pi)) -#define CLATD_VERSION "1.4" +#define CLATD_VERSION "1.5" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -38,16 +38,9 @@ struct tun_data; extern volatile sig_atomic_t running; -void configure_tun_ip(const struct tun_data *tunnel, const char *v4_addr, int mtu); void set_capability(uint64_t target_cap); -void drop_root_but_keep_caps(); -void open_sockets(struct tun_data *tunnel, uint32_t mark); +void drop_root_and_caps(); int ipv6_address_changed(const char *interface); -int configure_clat_ipv6_address(const struct tun_data *tunnel, const char *interface, - const char *src_addr); -int detect_mtu(const struct in6_addr *plat_subnet, uint32_t plat_suffix, uint32_t mark); -void configure_interface(const char *uplink_interface, const char *plat_prefix, const char *v4_addr, - const char *v6, struct tun_data *tunnel, uint32_t mark); void event_loop(struct tun_data *tunnel); /* function: parse_int diff --git a/clatd_test.cpp b/clatd_test.cpp index c16a4dd..8eef738 100644 --- a/clatd_test.cpp +++ b/clatd_test.cpp @@ -19,6 +19,7 @@ #include <iostream> #include <arpa/inet.h> +#include <linux/if_packet.h> #include <netinet/in6.h> #include <stdio.h> #include <sys/uio.h> @@ -29,10 +30,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" } @@ -559,21 +560,6 @@ int get_transport_checksum(const uint8_t *packet) { } } -static tun_data makeTunData() { - // Create some fake but realistic-looking sockets so update_clat_ipv6_address doesn't balk. - return { - .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), - .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), - .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 { @@ -624,29 +610,6 @@ TEST_F(ClatdTest, TestIPv6PrefixEqual) { EXPECT_FALSE(ipv6_prefix_equal(&subnet2, &Global_Clatd_Config.ipv6_local_subnet)); } -TEST_F(ClatdTest, DetectMtu) { - // ::1 with bottom 32 bits set to 1 is still ::1 which routes via lo with mtu of 64KiB - ASSERT_EQ(detect_mtu(&in6addr_loopback, htonl(1), 0 /*MARK_UNSET*/), 65536); -} - -TEST_F(ClatdTest, ConfigureTunIpManual) { - // 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 */, 1472); - 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); - - v4Iface.destroy(); -} - TEST_F(ClatdTest, DataSanitycheck) { // Sanity checks the data. uint8_t v4_header[] = { IPV4_UDP_HEADER }; @@ -874,7 +837,20 @@ TEST_F(ClatdTest, TranslateChecksumNeutral) { "UDP/IPv4 -> UDP/IPv6 checksum neutral"); } -TEST_F(ClatdTest, GetInterfaceIp) { +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(); @@ -882,50 +858,8 @@ TEST_F(ClatdTest, GetInterfaceIp) { 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) { - 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; - socklen_t len = sizeof(sll); - 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) { // Configure the clat IPv6 address. - struct tun_data tunnel = { - .read_fd6 = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)), - .write_fd6 = socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW), - }; const char *ifname = sTun.name().c_str(); in6_addr myaddr = sTun.srcAddr(); @@ -933,7 +867,7 @@ TEST_F(ClatdTest, Ipv6AddressChanged) { char addrstr[INET6_ADDRSTRLEN]; ASSERT_NE(nullptr, inet_ntop(AF_INET6, &myaddr, addrstr, sizeof(addrstr))); - ASSERT_EQ(1, configure_clat_ipv6_address(&tunnel, ifname, addrstr)); + Global_Clatd_Config.ipv6_local_subnet = myaddr; EXPECT_EQ(0, ipv6_address_changed(ifname)); EXPECT_EQ(0, ipv6_address_changed(ifname)); @@ -21,12 +21,9 @@ #include <linux/if.h> #include <netinet/in.h> -#include "ring.h" - struct tun_data { char device4[IFNAMSIZ]; int read_fd6, write_fd6, fd4; - struct packet_ring ring; }; struct clat_config { @@ -15,9 +15,7 @@ * * dump.c - print various headers for debugging */ -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> +#include "dump.h" #include <arpa/inet.h> #include <linux/icmp.h> @@ -29,12 +27,13 @@ #include <netinet/ip_icmp.h> #include <netinet/tcp.h> #include <netinet/udp.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> -#include "netutils/checksum.h" - +#include "checksum.h" #include "clatd.h" #include "debug.h" -#include "dump.h" #include "logging.h" #if CLAT_DEBUG @@ -15,21 +15,26 @@ * * 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 <linux/if_addr.h> -#include <linux/rtnetlink.h> -#include <netlink/handlers.h> -#include <netlink/msg.h> - -#include "getaddr.h" #include "logging.h" -#include "netlink_msg.h" -// shared state between getinterface_ip and getaddr_cb +// 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; @@ -37,57 +42,97 @@ struct target { int foundip; }; -/* function: getaddr_cb - * callback for getinterface_ip - * msg - netlink message - * data - (struct target) info for which address we're looking for +/* 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 int getaddr_cb(struct nl_msg *msg, void *data) { +static void parse_ifaddrmsg(struct nlmsghdr *nh, struct target *targ_p) { struct ifaddrmsg *ifa_p; struct rtattr *rta_p; int rta_len; - struct target *targ_p = (struct target *)data; - ifa_p = (struct ifaddrmsg *)nlmsg_data(nlmsg_hdr(msg)); + ifa_p = (struct ifaddrmsg *)NLMSG_DATA(nh); rta_p = (struct rtattr *)IFA_RTA(ifa_p); - if (ifa_p->ifa_index != targ_p->ifindex) return NL_OK; + if (ifa_p->ifa_index != targ_p->ifindex) return; - if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return NL_OK; + if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return; - rta_len = RTM_PAYLOAD(nlmsg_hdr(msg)); + 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 NL_OK; + 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 NL_OK; + return; } break; } } - - return NL_OK; } -/* function: error_handler - * error callback for getinterface_ip - * nla - source of the error message - * err - netlink message - * arg - (struct target) info for which address we're looking for - */ -static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, - __attribute__((unused)) struct nlmsgerr *err, - __attribute__((unused)) void *arg) { - return NL_OK; +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 @@ -97,42 +142,28 @@ static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, * family - family */ union anyip *getinterface_ip(const char *interface, int family) { - struct ifaddrmsg ifa; - struct nl_cb *callbacks = NULL; - struct target targ; union anyip *retval = NULL; + struct target targ = { + .family = family, + .foundip = 0, + .ifindex = if_nametoindex(interface), + }; - targ.family = family; - targ.foundip = 0; - targ.ifindex = if_nametoindex(interface); if (targ.ifindex == 0) { return NULL; // interface not found } - memset(&ifa, 0, sizeof(ifa)); - ifa.ifa_family = targ.family; - - callbacks = nl_cb_alloc(NL_CB_DEFAULT); - if (!callbacks) { - goto cleanup; - } - nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, getaddr_cb, &targ); - nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, &targ); - - // sends message and waits for a response - send_ifaddrmsg(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &ifa, callbacks); + // 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"); - goto cleanup; + return NULL; } memcpy(retval, &targ.ip, sizeof(union anyip)); } -cleanup: - if (callbacks) nl_cb_put(callbacks); - return retval; } @@ -18,6 +18,8 @@ #ifndef __GETADDR_H__ #define __GETADDR_H__ +#include <netinet/in.h> + union anyip { struct in6_addr ip6; struct in_addr ip4; @@ -17,8 +17,7 @@ */ #include <string.h> -#include "netutils/checksum.h" - +#include "checksum.h" #include "debug.h" #include "dump.h" #include "logging.h" @@ -15,12 +15,10 @@ * * ipv6.c - takes ipv6 packets, finds their headers, and then calls translation functions on them */ -#include <string.h> - #include <arpa/inet.h> +#include <string.h> -#include "netutils/checksum.h" - +#include "checksum.h" #include "config.h" #include "debug.h" #include "dump.h" @@ -25,13 +25,10 @@ #include <sys/capability.h> #include <unistd.h> -#include <netid_client.h> // For MARK_UNSET. - #include "clatd.h" #include "common.h" #include "config.h" #include "logging.h" -#include "setif.h" #define DEVICEPREFIX "v4-" @@ -49,8 +46,9 @@ void print_help() { printf("-p [plat prefix]\n"); printf("-4 [IPv4 address]\n"); printf("-6 [IPv6 address]\n"); - printf("-m [socket mark]\n"); printf("-t [tun file descriptor number]\n"); + printf("-r [read socket descriptor number]\n"); + printf("-w [write socket descriptor number]\n"); } /* function: main @@ -59,12 +57,12 @@ void print_help() { int main(int argc, char **argv) { struct tun_data tunnel; int opt; - char *uplink_interface = NULL, *plat_prefix = NULL, *mark_str = NULL; - char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL; - uint32_t mark = MARK_UNSET; + char *uplink_interface = NULL, *plat_prefix = NULL; + char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *read_sock_str = NULL, + *write_sock_str = NULL; unsigned len; - while ((opt = getopt(argc, argv, "i:p:4:6:m:t:h")) != -1) { + while ((opt = getopt(argc, argv, "i:p:4:6:t:r:w:h")) != -1) { switch (opt) { case 'i': uplink_interface = optarg; @@ -78,12 +76,15 @@ int main(int argc, char **argv) { case '6': v6_addr = optarg; break; - case 'm': - mark_str = optarg; - break; case 't': tunfd_str = optarg; break; + case 'r': + read_sock_str = optarg; + break; + case 'w': + write_sock_str = optarg; + break; case 'h': print_help(); exit(0); @@ -98,11 +99,6 @@ int main(int argc, char **argv) { exit(1); } - if (mark_str != NULL && !parse_unsigned(mark_str, &mark)) { - logmsg(ANDROID_LOG_FATAL, "invalid mark %s", mark_str); - exit(1); - } - if (tunfd_str != NULL && !parse_int(tunfd_str, &tunnel.fd4)) { logmsg(ANDROID_LOG_FATAL, "invalid tunfd %s", tunfd_str); exit(1); @@ -112,30 +108,52 @@ int main(int argc, char **argv) { exit(1); } + if (read_sock_str != NULL && !parse_int(read_sock_str, &tunnel.read_fd6)) { + logmsg(ANDROID_LOG_FATAL, "invalid read socket %s", read_sock_str); + exit(1); + } + if (!tunnel.read_fd6) { + logmsg(ANDROID_LOG_FATAL, "no read_fd6 specified on commandline."); + exit(1); + } + + if (write_sock_str != NULL && !parse_int(write_sock_str, &tunnel.write_fd6)) { + logmsg(ANDROID_LOG_FATAL, "invalid write socket %s", write_sock_str); + exit(1); + } + if (!tunnel.write_fd6) { + logmsg(ANDROID_LOG_FATAL, "no write_fd6 specified on commandline."); + exit(1); + } + len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface); if (len >= sizeof(tunnel.device4)) { logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4); exit(1); } - logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s mark=%s plat=%s v4=%s v6=%s", - CLATD_VERSION, uplink_interface, 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(); + Global_Clatd_Config.native_ipv6_interface = uplink_interface; + if (!plat_prefix || inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) { + logmsg(ANDROID_LOG_FATAL, "invalid IPv6 address specified for plat prefix: %s", plat_prefix); + exit(1); + } - // open our raw sockets before dropping privs - open_sockets(&tunnel, mark); + if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) { + logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr); + exit(1); + } - // keeps only admin capability - set_capability(1 << CAP_NET_ADMIN); + if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) { + logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr); + exit(1); + } - configure_interface(uplink_interface, plat_prefix, v4_addr, v6_addr, &tunnel, mark); + logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s plat=%s v4=%s v6=%s", CLATD_VERSION, + uplink_interface, plat_prefix ? plat_prefix : "(none)", v4_addr ? v4_addr : "(none)", + v6_addr ? v6_addr : "(none)"); - // Drop all remaining capabilities. - set_capability(0); + // run under a regular user with no capabilities + drop_root_and_caps(); // Loop until someone sends us a signal or brings down the tun interface. if (signal(SIGTERM, stop_loop) == SIG_ERR) { @@ -146,11 +164,6 @@ int main(int argc, char **argv) { event_loop(&tunnel); logmsg(ANDROID_LOG_INFO, "Shutting down clat on %s", uplink_interface); - del_anycast_address(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet); - - close(tunnel.write_fd6); - close(tunnel.read_fd6); - close(tunnel.fd4); if (running) { logmsg(ANDROID_LOG_INFO, "Clatd on %s waiting for SIGTERM", uplink_interface); diff --git a/netlink_callbacks.c b/netlink_callbacks.c deleted file mode 100644 index 804976d..0000000 --- a/netlink_callbacks.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2012 Daniel Drown <dan-android@drown.org> - * - * 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. - * - * netlink_callbacks.c - generic callbacks for netlink responses - */ -#include <net/if.h> -#include <netinet/in.h> - -#include <linux/rtnetlink.h> -#include <netlink/handlers.h> -#include <netlink/msg.h> - -/* function: ack_handler - * generic netlink callback for ack messages - * msg - netlink message - * data - pointer to an int, stores the success code - */ -static int ack_handler(__attribute__((unused)) struct nl_msg *msg, void *data) { - int *retval = data; - *retval = 0; - return NL_OK; -} - -/* function: error_handler - * generic netlink callback for error messages - * nla - error source - * err - netlink error message - * arg - pointer to an int, stores the error code - */ -static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, struct nlmsgerr *err, - void *arg) { - int *retval = arg; - if (err->error < 0) { - *retval = err->error; - } else { - *retval = 0; // NLMSG_ERROR used as reply type on no error - } - return NL_OK; -} - -/* function: alloc_ack_callbacks - * allocates a set of netlink callbacks. returns NULL on failure. callbacks will modify retval - * with <0 meaning failure retval - shared state between caller and callback functions - */ -struct nl_cb *alloc_ack_callbacks(int *retval) { - struct nl_cb *callbacks; - - callbacks = nl_cb_alloc(NL_CB_DEFAULT); - if (!callbacks) { - return NULL; - } - nl_cb_set(callbacks, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, retval); - nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, retval); - return callbacks; -} diff --git a/netlink_callbacks.h b/netlink_callbacks.h deleted file mode 100644 index 298ad3e..0000000 --- a/netlink_callbacks.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2012 Daniel Drown <dan-android@drown.org> - * - * 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. - * - * netlink_callbacks.h - callbacks for netlink responses - */ - -#ifndef __NETLINK_CALLBACKS_H__ -#define __NETLINK_CALLBACKS_H__ - -struct nl_cb *alloc_ack_callbacks(int *retval); - -#endif diff --git a/netlink_msg.c b/netlink_msg.c deleted file mode 100644 index be76ecd..0000000 --- a/netlink_msg.c +++ /dev/null @@ -1,182 +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. - * - * netlink_msg.c - send an ifaddrmsg/ifinfomsg/rtmsg via netlink - */ - -#include <errno.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <netinet/in.h> -#include <string.h> - -#include <netlink-private/object-api.h> -#include <netlink-private/types.h> -#include <netlink/msg.h> -#include <netlink/netlink.h> -#include <netlink/socket.h> - -#include "netlink_callbacks.h" -#include "netlink_msg.h" - -/* function: family_size - * returns the size of the address structure for the given family, or 0 on error - * family - AF_INET or AF_INET6 - */ -size_t inet_family_size(int family) { - if (family == AF_INET) { - return sizeof(struct in_addr); - } else if (family == AF_INET6) { - return sizeof(struct in6_addr); - } else { - return 0; - } -} - -/* function: nlmsg_alloc_generic - * allocates a netlink message with the given struct inside of it. returns NULL on failure - * type - netlink message type - * flags - netlink message flags - * payload_struct - pointer to a struct to add to netlink message - * payload_len - bytelength of structure - */ -struct nl_msg *nlmsg_alloc_generic(uint16_t type, uint16_t flags, void *payload_struct, - size_t payload_len) { - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) { - return NULL; - } - - if ((sizeof(struct nl_msg) + payload_len) > msg->nm_size) { - nlmsg_free(msg); - return NULL; - } - - msg->nm_nlh->nlmsg_len = NLMSG_LENGTH(payload_len); - msg->nm_nlh->nlmsg_flags = flags; - msg->nm_nlh->nlmsg_type = type; - - memcpy(nlmsg_data(msg->nm_nlh), payload_struct, payload_len); - - return msg; -} - -/* function: nlmsg_alloc_ifaddr - * allocates a netlink message with a struct ifaddrmsg inside of it. returns NULL on failure - * type - netlink message type - * flags - netlink message flags - * ifa - ifaddrmsg to copy into the new netlink message - */ -struct nl_msg *nlmsg_alloc_ifaddr(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa) { - return nlmsg_alloc_generic(type, flags, ifa, sizeof(*ifa)); -} - -/* function: nlmsg_alloc_ifinfo - * allocates a netlink message with a struct ifinfomsg inside of it. returns NULL on failure - * type - netlink message type - * flags - netlink message flags - * ifi - ifinfomsg to copy into the new netlink message - */ -struct nl_msg *nlmsg_alloc_ifinfo(uint16_t type, uint16_t flags, struct ifinfomsg *ifi) { - return nlmsg_alloc_generic(type, flags, ifi, sizeof(*ifi)); -} - -/* function: nlmsg_alloc_rtmsg - * allocates a netlink message with a struct rtmsg inside of it. returns NULL on failure - * type - netlink message type - * flags - netlink message flags - * rt - rtmsg to copy into the new netlink message - */ -struct nl_msg *nlmsg_alloc_rtmsg(uint16_t type, uint16_t flags, struct rtmsg *rt) { - return nlmsg_alloc_generic(type, flags, rt, sizeof(*rt)); -} - -/* function: netlink_set_kernel_only - * sets a socket to receive messages only from the kernel - * sock - socket to connect - */ -int netlink_set_kernel_only(struct nl_sock *nl_sk) { - struct sockaddr_nl addr = { AF_NETLINK, 0, 0, 0 }; - - if (!nl_sk) { - return -EFAULT; - } - - int sockfd = nl_socket_get_fd(nl_sk); - return connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); -} - -/* function: send_netlink_msg - * sends a netlink message, reads a response, and hands the response(s) to the callbacks - * msg - netlink message to send - * callbacks - callbacks to use on responses - */ -void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks) { - struct nl_sock *nl_sk; - - nl_sk = nl_socket_alloc(); - if (!nl_sk) goto cleanup; - - if (nl_connect(nl_sk, NETLINK_ROUTE) != 0) goto cleanup; - - if (nl_send_auto_complete(nl_sk, msg) < 0) goto cleanup; - - if (netlink_set_kernel_only(nl_sk) < 0) goto cleanup; - - nl_recvmsgs(nl_sk, callbacks); - -cleanup: - if (nl_sk) nl_socket_free(nl_sk); -} - -/* function: send_ifaddrmsg - * sends a netlink/ifaddrmsg message and hands the responses to the callbacks - * type - netlink message type - * flags - netlink message flags - * ifa - ifaddrmsg to send - * callbacks - callbacks to use with the responses - */ -void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks) { - struct nl_msg *msg = NULL; - - msg = nlmsg_alloc_ifaddr(type, flags, ifa); - if (!msg) return; - - send_netlink_msg(msg, callbacks); - - nlmsg_free(msg); -} - -/* function: netlink_sendrecv - * send a nl_msg and return an int status - only supports OK/ERROR responses - * msg - msg to send - */ -int netlink_sendrecv(struct nl_msg *msg) { - struct nl_cb *callbacks = NULL; - int retval = -EIO; - - callbacks = alloc_ack_callbacks(&retval); - if (!callbacks) { - return -ENOMEM; - } - - send_netlink_msg(msg, callbacks); - - nl_cb_put(callbacks); - - return retval; -} diff --git a/netlink_msg.h b/netlink_msg.h deleted file mode 100644 index 13e1f28..0000000 --- a/netlink_msg.h +++ /dev/null @@ -1,30 +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. - * - * netlink_msg.h - send an ifaddrmsg/ifinfomsg via netlink - */ -#ifndef __NETLINK_IFMSG_H__ -#define __NETLINK_IFMSG_H__ - -size_t inet_family_size(int family); -struct nl_msg *nlmsg_alloc_ifaddr(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa); -struct nl_msg *nlmsg_alloc_ifinfo(uint16_t type, uint16_t flags, struct ifinfomsg *ifi); -struct nl_msg *nlmsg_alloc_rtmsg(uint16_t type, uint16_t flags, struct rtmsg *rt); -void send_netlink_msg(struct nl_msg *msg, struct nl_cb *callbacks); -void send_ifaddrmsg(uint16_t type, uint16_t flags, struct ifaddrmsg *ifa, struct nl_cb *callbacks); -int netlink_sendrecv(struct nl_msg *msg); -int netlink_set_kernel_only(struct nl_sock *nl_sk); - -#endif @@ -1,128 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * 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. - * - * ring.c - packet ring buffer functions - */ - -#include <arpa/inet.h> -#include <errno.h> -#include <linux/if.h> -#include <linux/if_packet.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/socket.h> - -#include "config.h" -#include "logging.h" -#include "ring.h" -#include "translate.h" - -int ring_create(struct tun_data *tunnel) { - // Will eventually be bound to htons(ETH_P_IPV6) protocol, - // but only after appropriate bpf filter is attached. - int packetsock = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (packetsock < 0) { - logmsg(ANDROID_LOG_FATAL, "packet socket failed: %s", strerror(errno)); - return -1; - } - - int ver = TPACKET_V2; - if (setsockopt(packetsock, SOL_PACKET, PACKET_VERSION, (void *)&ver, sizeof(ver))) { - logmsg(ANDROID_LOG_FATAL, "setsockopt(PACKET_VERSION, %d) failed: %s", ver, strerror(errno)); - return -1; - } - - int on = 1; - if (setsockopt(packetsock, SOL_PACKET, PACKET_LOSS, (void *)&on, sizeof(on))) { - logmsg(ANDROID_LOG_WARN, "PACKET_LOSS failed: %s", strerror(errno)); - } - - struct packet_ring *ring = &tunnel->ring; - ring->numblocks = TP_NUM_BLOCKS; - - int total_frames = TP_FRAMES * ring->numblocks; - - struct tpacket_req req = { - .tp_frame_size = TP_FRAME_SIZE, // Frame size. - .tp_block_size = TP_BLOCK_SIZE, // Frames per block. - .tp_block_nr = ring->numblocks, // Number of blocks. - .tp_frame_nr = total_frames, // Total frames. - }; - - if (setsockopt(packetsock, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) { - logmsg(ANDROID_LOG_FATAL, "PACKET_RX_RING failed: %s", strerror(errno)); - return -1; - } - - size_t buflen = TP_BLOCK_SIZE * ring->numblocks; - ring->base = mmap(NULL, buflen, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED | MAP_POPULATE, - packetsock, 0); - if (ring->base == MAP_FAILED) { - logmsg(ANDROID_LOG_FATAL, "mmap %lu failed: %s", buflen, strerror(errno)); - return -1; - } - - ring->block = 0; - ring->slot = 0; - ring->numslots = TP_BLOCK_SIZE / TP_FRAME_SIZE; - ring->next = (struct tpacket2_hdr *)ring->base; - - logmsg(ANDROID_LOG_INFO, "Using ring buffer with %d frames (%d bytes) at %p", total_frames, - buflen, ring->base); - - return packetsock; -} - -/* function: ring_advance - * advances to the next position in the packet ring - * ring - packet ring buffer - */ -static struct tpacket2_hdr *ring_advance(struct packet_ring *ring) { - uint8_t *next = (uint8_t *)ring->next; - - ring->slot++; - next += TP_FRAME_SIZE; - - if (ring->slot == ring->numslots) { - ring->slot = 0; - ring->block++; - - if (ring->block < ring->numblocks) { - next += TP_FRAME_GAP; - } else { - ring->block = 0; - next = (uint8_t *)ring->base; - } - } - - ring->next = (struct tpacket2_hdr *)next; - return ring->next; -} - -/* function: ring_read - * reads a packet from the ring buffer 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 ring_read(struct packet_ring *ring, int write_fd, int to_ipv6) { - struct tpacket2_hdr *tp = ring->next; - if (tp->tp_status & TP_STATUS_USER) { - uint8_t *packet = ((uint8_t *)tp) + tp->tp_net; - translate_packet(write_fd, to_ipv6, packet, tp->tp_len); - tp->tp_status = TP_STATUS_KERNEL; - tp = ring_advance(ring); - } -} @@ -1,55 +0,0 @@ -/* - * Copyright 2014 The Android Open Source Project - * - * 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. - * - * ring.c - packet ring buffer functions - */ -#ifndef __RING_H__ -#define __RING_H__ - -#include <linux/if.h> -#include <linux/if_packet.h> - -#include "clatd.h" - -struct tun_data; - -// Frame size. Must be a multiple of TPACKET_ALIGNMENT (=16) -// Why the 16? http://lxr.free-electrons.com/source/net/packet/af_packet.c?v=3.4#L1764 -#define TP_FRAME_SIZE (TPACKET_ALIGN(MAXMTU) + TPACKET_ALIGN(TPACKET2_HDRLEN) + 16) - -// Block size. Must be a multiple of the page size, and a power of two for efficient memory use. -#define TP_BLOCK_SIZE 65536 - -// In order to save memory, our frames are not an exact divider of the block size. Therefore, the -// mmaped region will have gaps corresponding to the empty space at the end of each block. -#define TP_FRAMES (TP_BLOCK_SIZE / TP_FRAME_SIZE) -#define TP_FRAME_GAP (TP_BLOCK_SIZE % TP_FRAME_SIZE) - -// TODO: Make this configurable. This requires some refactoring because the packet socket is -// opened before we drop privileges, but the configuration file is read after. A value of 16 -// results in 656 frames (1048576 bytes). -#define TP_NUM_BLOCKS 16 - -struct packet_ring { - uint8_t *base; - struct tpacket2_hdr *next; - int slot, numslots; - int block, numblocks; -}; - -int ring_create(struct tun_data *tunnel); -void ring_read(struct packet_ring *ring, int write_fd, int to_ipv6); - -#endif diff --git a/setif.c b/setif.c deleted file mode 100644 index 184a46b..0000000 --- a/setif.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2012 Daniel Drown <dan-android@drown.org> - * - * 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. - * - * setif.c - network interface configuration - */ -#include <errno.h> -#include <net/if.h> -#include <netinet/in.h> - -#include <linux/rtnetlink.h> -#include <netlink/handlers.h> -#include <netlink/msg.h> - -#include "logging.h" -#include "netlink_msg.h" - -#define DEBUG_OPTNAME(a) \ - case (a): { \ - optname = #a; \ - break; \ - } - -/* function: add_address - * adds an IP address to/from an interface, returns 0 on success and <0 on failure - * ifname - name of interface to change - * family - address family (AF_INET, AF_INET6) - * address - pointer to a struct in_addr or in6_addr - * prefixlen - bitlength of network (example: 24 for AF_INET's 255.255.255.0) - * broadcast - broadcast address (only for AF_INET, ignored for AF_INET6) - */ -int add_address(const char *ifname, int family, const void *address, int prefixlen, - const void *broadcast) { - int retval; - size_t addr_size; - struct ifaddrmsg ifa; - struct nl_msg *msg = NULL; - - addr_size = inet_family_size(family); - if (addr_size == 0) { - retval = -EAFNOSUPPORT; - goto cleanup; - } - - memset(&ifa, 0, sizeof(ifa)); - if (!(ifa.ifa_index = if_nametoindex(ifname))) { - retval = -ENODEV; - goto cleanup; - } - ifa.ifa_family = family; - ifa.ifa_prefixlen = prefixlen; - ifa.ifa_scope = RT_SCOPE_UNIVERSE; - - msg = - nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa); - if (!msg) { - retval = -ENOMEM; - goto cleanup; - } - - if (nla_put(msg, IFA_LOCAL, addr_size, address) < 0) { - retval = -ENOMEM; - goto cleanup; - } - if (family == AF_INET6) { - // AF_INET6 gets IFA_LOCAL + IFA_ADDRESS - if (nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) { - retval = -ENOMEM; - goto cleanup; - } - } else if (family == AF_INET) { - // AF_INET gets IFA_LOCAL + IFA_BROADCAST - if (nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) { - retval = -ENOMEM; - goto cleanup; - } - } else { - retval = -EAFNOSUPPORT; - goto cleanup; - } - - retval = netlink_sendrecv(msg); - -cleanup: - if (msg) nlmsg_free(msg); - - return retval; -} - -/* function: if_up - * sets interface link state to up and sets mtu, returns 0 on success and <0 on failure - * ifname - interface name to change - * mtu - new mtu - */ -int if_up(const char *ifname, int mtu) { - int retval = -1; - struct ifinfomsg ifi; - struct nl_msg *msg = NULL; - - memset(&ifi, 0, sizeof(ifi)); - if (!(ifi.ifi_index = if_nametoindex(ifname))) { - retval = -ENODEV; - goto cleanup; - } - ifi.ifi_change = IFF_UP; - ifi.ifi_flags = IFF_UP; - - msg = nlmsg_alloc_ifinfo(RTM_SETLINK, NLM_F_ACK | NLM_F_REQUEST | NLM_F_ROOT, &ifi); - if (!msg) { - retval = -ENOMEM; - goto cleanup; - } - - if (nla_put(msg, IFLA_MTU, 4, &mtu) < 0) { - retval = -ENOMEM; - goto cleanup; - } - - retval = netlink_sendrecv(msg); - -cleanup: - if (msg) nlmsg_free(msg); - - return retval; -} - -static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int ifindex) { - struct ipv6_mreq mreq = { *addr, ifindex }; - char *optname; - int ret; - - switch (what) { - DEBUG_OPTNAME(IPV6_JOIN_ANYCAST) - DEBUG_OPTNAME(IPV6_LEAVE_ANYCAST) - default: - optname = "???"; - break; - } - - ret = setsockopt(sock, SOL_IPV6, what, &mreq, sizeof(mreq)); - if (ret) { - logmsg(ANDROID_LOG_ERROR, "%s: setsockopt(%s): %s", __func__, optname, strerror(errno)); - } - - return ret; -} - -/* function: add_anycast_address - * adds an anycast IPv6 address to an interface, returns 0 on success and <0 on failure - * sock - the socket to add the address to - * addr - the IP address to add - * ifname - name of interface to add the address to - */ -int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) { - int ifindex; - - ifindex = if_nametoindex(ifname); - if (!ifindex) { - logmsg(ANDROID_LOG_ERROR, "%s: unknown ifindex for interface %s", __func__, ifname); - return -ENODEV; - } - - return do_anycast_setsockopt(sock, IPV6_JOIN_ANYCAST, addr, ifindex); -} - -/* function: del_anycast_address - * removes an anycast IPv6 address from the system, returns 0 on success and <0 on failure - * sock - the socket to remove from, must have had the address added via add_anycast_address - * addr - the IP address to remove - */ -int del_anycast_address(int sock, struct in6_addr *addr) { - return do_anycast_setsockopt(sock, IPV6_LEAVE_ANYCAST, addr, 0); -} diff --git a/setif.h b/setif.h deleted file mode 100644 index 2d0adb1..0000000 --- a/setif.h +++ /dev/null @@ -1,28 +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. - * - * setif.h - network interface configuration - */ -#ifndef __SETIF_H__ -#define __SETIF_H__ - -int add_address(const char *ifname, int family, const void *address, int cidr, - const void *broadcast); -int if_up(const char *ifname, int mtu); - -int add_anycast_address(int sock, const struct in6_addr *addr, const char *interface); -int del_anycast_address(int sock, const struct in6_addr *addr); - -#endif diff --git a/translate.c b/translate.c index 728acc3..22830d8 100644 --- a/translate.c +++ b/translate.c @@ -15,17 +15,17 @@ * * translate.c - CLAT functions / partial implementation of rfc6145 */ -#include <string.h> +#include "translate.h" -#include "netutils/checksum.h" +#include <string.h> +#include "checksum.h" #include "clatd.h" #include "common.h" #include "config.h" #include "debug.h" #include "icmp.h" #include "logging.h" -#include "translate.h" /* function: packet_checksum * calculates the checksum over all the packet components starting from pos diff --git a/vendor-464xlat.rc b/vendor-464xlat.rc deleted file mode 100644 index 609531d..0000000 --- a/vendor-464xlat.rc +++ /dev/null @@ -1,15 +0,0 @@ -# Certain vendors disable 464xlat by setting a vendor property. -# The connectivity code in the Tethering APEX needs to disable -# 464xlat when the property is set, but it is only allowed to -# access non-vendor system properties. So copy the property to -# a property available to system APIs in android.sysprop. -# -# Arguably this script should live close to the code that uses -# it, but scrips in APEXes are not allowed to use "on property". -# So put it here close to clatd, which is at least related to -# 464xlat. -on property:persist.vendor.net.doxlat=true - setprop net.464xlat.cellular.enabled true - -on property:persist.vendor.net.doxlat=false - setprop net.464xlat.cellular.enabled false |