diff --git a/service/native/libs/libclat/Android.bp b/service/native/libs/libclat/Android.bp index 9fe441ac70..85407871f2 100644 --- a/service/native/libs/libclat/Android.bp +++ b/service/native/libs/libclat/Android.bp @@ -39,6 +39,11 @@ cc_test { "libbase", "libclat", "libip_checksum", + "libnetd_test_tun_interface", ], - shared_libs: ["liblog"], + shared_libs: [ + "liblog", + "libnetutils", + ], + require_root: true, } \ No newline at end of file diff --git a/service/native/libs/libclat/clatutils.cpp b/service/native/libs/libclat/clatutils.cpp index 8f037ad07e..4a125ba9ff 100644 --- a/service/native/libs/libclat/clatutils.cpp +++ b/service/native/libs/libclat/clatutils.cpp @@ -17,6 +17,9 @@ #include "libclat/clatutils.h" #include +#include +#include +#include #include #include #include @@ -26,6 +29,10 @@ extern "C" { #include "checksum.h" } +// Sync from external/android-clat/clatd.h +#define MAXMTU 65536 +#define PACKETLEN (MAXMTU + sizeof(struct tun_pi)) + // Sync from system/netd/include/netid_client.h. #define MARK_UNSET 0u @@ -203,6 +210,59 @@ int detect_mtu(const struct in6_addr* plat_subnet, uint32_t plat_suffix, uint32_ return mtu; } +/* function: configure_packet_socket + * Binds the packet socket and attaches the receive filter to it. + * sock - the socket to configure + * addr - the IP address to filter + * ifindex - index of interface to add the filter to + * returns: 0 on success, -errno on failure + */ +int configure_packet_socket(int sock, in6_addr* addr, int ifindex) { + uint32_t* ipv6 = addr->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))) { + int res = errno; + ALOGE("attach packet filter failed: %s", strerror(errno)); + return -res; + } + + struct sockaddr_ll sll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_IPV6), + .sll_ifindex = ifindex, + .sll_pkttype = + PACKET_OTHERHOST, // The 464xlat IPv6 address is not assigned to the kernel. + }; + if (bind(sock, (struct sockaddr*)&sll, sizeof(sll))) { + int res = errno; + ALOGE("binding packet socket: %s", strerror(errno)); + return -res; + } + + return 0; +} + } // namespace clat } // namespace net } // namespace android diff --git a/service/native/libs/libclat/clatutils_test.cpp b/service/native/libs/libclat/clatutils_test.cpp index bb9678d665..4153e19d37 100644 --- a/service/native/libs/libclat/clatutils_test.cpp +++ b/service/native/libs/libclat/clatutils_test.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include "tun_interface.h" extern "C" { #include "checksum.h" @@ -29,6 +32,7 @@ namespace android { namespace net { namespace clat { +using android::net::TunInterface; using base::StringPrintf; class ClatUtils : public ::testing::Test {}; @@ -155,6 +159,29 @@ TEST_F(ClatUtils, DetectMtu) { ASSERT_EQ(detect_mtu(&in6addr_loopback, htonl(1), 0 /*MARK_UNSET*/), 65536); } +TEST_F(ClatUtils, ConfigurePacketSocket) { + // Create an interface for configure_packet_socket to attach socket filter to. + TunInterface v6Iface; + ASSERT_EQ(0, v6Iface.init()); + + int s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6)); + EXPECT_LE(0, s); + struct in6_addr addr6; + EXPECT_EQ(1, inet_pton(AF_INET6, "2001:db8::f00", &addr6)); + EXPECT_EQ(0, configure_packet_socket(s, &addr6, v6Iface.ifindex())); + + // 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(s, reinterpret_cast(&sll), &len)); + EXPECT_EQ(htons(ETH_P_IPV6), sll.sll_protocol); + EXPECT_EQ(sll.sll_ifindex, v6Iface.ifindex()); + + close(s); + v6Iface.destroy(); +} + } // namespace clat } // namespace net } // namespace android diff --git a/service/native/libs/libclat/include/libclat/clatutils.h b/service/native/libs/libclat/include/libclat/clatutils.h index c8f83f16b4..812c86eb21 100644 --- a/service/native/libs/libclat/include/libclat/clatutils.h +++ b/service/native/libs/libclat/include/libclat/clatutils.h @@ -26,6 +26,7 @@ void makeChecksumNeutral(in6_addr* v6, const in_addr v4, const in6_addr& nat64Pr int generateIpv6Address(const char* iface, const in_addr v4, const in6_addr& nat64Prefix, in6_addr* v6); int detect_mtu(const struct in6_addr* plat_subnet, uint32_t plat_suffix, uint32_t mark); +int configure_packet_socket(int sock, in6_addr* addr, int ifindex); // For testing typedef bool (*isIpv4AddrFreeFn)(in_addr_t);