[CLATJ#6] libclat: move configure_packet_socket() from netd to apex

configure_packet_socket() is moved from ClatdController. Only change
the failure return value from errno to -errno. The remaining parts
are the same.

The test is modified from ClatdControllerTest::ConfigureIpv6Address.
Simply set socket filter to an interface and 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.

Bug: 212345928
Test: build and boot
      atest libclat_test
Change-Id: Ia33c21051b040829c7d2aa55ed5b7de8766c98c5
This commit is contained in:
Hungming Chen 2021-12-17 21:12:53 +08:00
commit 8ff032b990
4 changed files with 94 additions and 1 deletions

View file

@ -39,6 +39,11 @@ cc_test {
"libbase",
"libclat",
"libip_checksum",
"libnetd_test_tun_interface",
],
shared_libs: ["liblog"],
shared_libs: [
"liblog",
"libnetutils",
],
require_root: true,
}

View file

@ -17,6 +17,9 @@
#include "libclat/clatutils.h"
#include <errno.h>
#include <linux/filter.h>
#include <linux/if_packet.h>
#include <linux/if_tun.h>
#include <log/log.h>
#include <stdlib.h>
#include <string.h>
@ -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

View file

@ -17,6 +17,9 @@
#include <android-base/stringprintf.h>
#include <arpa/inet.h>
#include <gtest/gtest.h>
#include <linux/if_packet.h>
#include <linux/if_tun.h>
#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<sockaddr*>(&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

View file

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