Merge changes I2c3242f8,Iaa67d5ae am: b3bf5fedab
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/2050859 Change-Id: I2b0a294752ae2b297ba0f70e5bf0596c0fa72c70 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
committed by
Automerger Merge Worker
commit
f23da94516
@@ -34,7 +34,6 @@
|
|||||||
#include <netjniutils/netjniutils.h>
|
#include <netjniutils/netjniutils.h>
|
||||||
#include <private/android_filesystem_config.h>
|
#include <private/android_filesystem_config.h>
|
||||||
|
|
||||||
#include "libclat/bpfhelper.h"
|
|
||||||
#include "libclat/clatutils.h"
|
#include "libclat/clatutils.h"
|
||||||
#include "nativehelper/scoped_utf_chars.h"
|
#include "nativehelper/scoped_utf_chars.h"
|
||||||
|
|
||||||
@@ -257,46 +256,6 @@ static void com_android_server_connectivity_ClatCoordinator_configurePacketSocke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int initTracker(const std::string& iface, const std::string& pfx96, const std::string& v4,
|
|
||||||
const std::string& v6, net::clat::ClatdTracker* output) {
|
|
||||||
strlcpy(output->iface, iface.c_str(), sizeof(output->iface));
|
|
||||||
output->ifIndex = if_nametoindex(iface.c_str());
|
|
||||||
if (output->ifIndex == 0) {
|
|
||||||
ALOGE("interface %s not found", output->iface);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned len = snprintf(output->v4iface, sizeof(output->v4iface),
|
|
||||||
"%s%s", DEVICEPREFIX, iface.c_str());
|
|
||||||
if (len >= sizeof(output->v4iface)) {
|
|
||||||
ALOGE("interface name too long '%s'", output->v4iface);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->v4ifIndex = if_nametoindex(output->v4iface);
|
|
||||||
if (output->v4ifIndex == 0) {
|
|
||||||
ALOGE("v4-interface %s not found", output->v4iface);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inet_pton(AF_INET6, pfx96.c_str(), &output->pfx96)) {
|
|
||||||
ALOGE("invalid IPv6 address specified for plat prefix: %s", pfx96.c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inet_pton(AF_INET, v4.c_str(), &output->v4)) {
|
|
||||||
ALOGE("Invalid IPv4 address %s", v4.c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inet_pton(AF_INET6, v6.c_str(), &output->v6)) {
|
|
||||||
ALOGE("Invalid source address %s", v6.c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static jint com_android_server_connectivity_ClatCoordinator_startClatd(
|
static jint com_android_server_connectivity_ClatCoordinator_startClatd(
|
||||||
JNIEnv* env, jobject clazz, jobject tunJavaFd, jobject readSockJavaFd,
|
JNIEnv* env, jobject clazz, jobject tunJavaFd, jobject readSockJavaFd,
|
||||||
jobject writeSockJavaFd, jstring iface, jstring pfx96, jstring v4, jstring v6) {
|
jobject writeSockJavaFd, jstring iface, jstring pfx96, jstring v4, jstring v6) {
|
||||||
@@ -458,14 +417,6 @@ static void com_android_server_connectivity_ClatCoordinator_stopClatd(JNIEnv* en
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!net::clat::initMaps()) {
|
|
||||||
net::clat::ClatdTracker tracker = {};
|
|
||||||
if (!initTracker(ifaceStr.c_str(), pfx96Str.c_str(), v4Str.c_str(), v6Str.c_str(),
|
|
||||||
&tracker)) {
|
|
||||||
net::clat::maybeStopBpf(tracker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stopClatdProcess(pid);
|
stopClatdProcess(pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,19 +19,12 @@ package {
|
|||||||
cc_library_static {
|
cc_library_static {
|
||||||
name: "libclat",
|
name: "libclat",
|
||||||
defaults: ["netd_defaults"],
|
defaults: ["netd_defaults"],
|
||||||
header_libs: [
|
|
||||||
"bpf_connectivity_headers",
|
|
||||||
"libbase_headers",
|
|
||||||
],
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"TcUtils.cpp", // TODO: move to frameworks/libs/net
|
|
||||||
"bpfhelper.cpp",
|
|
||||||
"clatutils.cpp",
|
"clatutils.cpp",
|
||||||
],
|
],
|
||||||
stl: "libc++_static",
|
stl: "libc++_static",
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"libip_checksum",
|
"libip_checksum",
|
||||||
"libnetdutils", // for netdutils/UidConstants.h in bpf_shared.h
|
|
||||||
],
|
],
|
||||||
shared_libs: ["liblog"],
|
shared_libs: ["liblog"],
|
||||||
export_include_dirs: ["include"],
|
export_include_dirs: ["include"],
|
||||||
@@ -43,11 +36,7 @@ cc_test {
|
|||||||
name: "libclat_test",
|
name: "libclat_test",
|
||||||
defaults: ["netd_defaults"],
|
defaults: ["netd_defaults"],
|
||||||
test_suites: ["device-tests"],
|
test_suites: ["device-tests"],
|
||||||
header_libs: [
|
|
||||||
"bpf_connectivity_headers",
|
|
||||||
],
|
|
||||||
srcs: [
|
srcs: [
|
||||||
"TcUtilsTest.cpp",
|
|
||||||
"clatutils_test.cpp",
|
"clatutils_test.cpp",
|
||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
@@ -55,8 +44,6 @@ cc_test {
|
|||||||
"libclat",
|
"libclat",
|
||||||
"libip_checksum",
|
"libip_checksum",
|
||||||
"libnetd_test_tun_interface",
|
"libnetd_test_tun_interface",
|
||||||
"libnetdutils", // for netdutils/UidConstants.h in bpf_shared.h
|
|
||||||
"libtcutils",
|
|
||||||
],
|
],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"liblog",
|
"liblog",
|
||||||
|
|||||||
@@ -1,390 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2019 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "TcUtils"
|
|
||||||
|
|
||||||
#include "libclat/TcUtils.h"
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <linux/if.h>
|
|
||||||
#include <linux/if_arp.h>
|
|
||||||
#include <linux/netlink.h>
|
|
||||||
#include <linux/pkt_cls.h>
|
|
||||||
#include <linux/pkt_sched.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <log/log.h>
|
|
||||||
|
|
||||||
#include "android-base/unique_fd.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace net {
|
|
||||||
|
|
||||||
using std::max;
|
|
||||||
|
|
||||||
// Sync from system/netd/server/NetlinkCommands.h
|
|
||||||
const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
|
|
||||||
const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
|
|
||||||
|
|
||||||
static int doSIOCGIF(const std::string& interface, int opt) {
|
|
||||||
base::unique_fd ufd(socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0));
|
|
||||||
|
|
||||||
if (ufd < 0) {
|
|
||||||
const int err = errno;
|
|
||||||
ALOGE("socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)");
|
|
||||||
return -err;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ifreq ifr = {};
|
|
||||||
// We use strncpy() instead of strlcpy() since kernel has to be able
|
|
||||||
// to handle non-zero terminated junk passed in by userspace anyway,
|
|
||||||
// and this way too long interface names (more than IFNAMSIZ-1 = 15
|
|
||||||
// characters plus terminating NULL) will not get truncated to 15
|
|
||||||
// characters and zero-terminated and thus potentially erroneously
|
|
||||||
// match a truncated interface if one were to exist.
|
|
||||||
strncpy(ifr.ifr_name, interface.c_str(), sizeof(ifr.ifr_name));
|
|
||||||
|
|
||||||
if (ioctl(ufd, opt, &ifr, sizeof(ifr))) return -errno;
|
|
||||||
|
|
||||||
if (opt == SIOCGIFHWADDR) return ifr.ifr_hwaddr.sa_family;
|
|
||||||
if (opt == SIOCGIFMTU) return ifr.ifr_mtu;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hardwareAddressType(const std::string& interface) {
|
|
||||||
return doSIOCGIF(interface, SIOCGIFHWADDR);
|
|
||||||
}
|
|
||||||
|
|
||||||
int deviceMTU(const std::string& interface) {
|
|
||||||
return doSIOCGIF(interface, SIOCGIFMTU);
|
|
||||||
}
|
|
||||||
|
|
||||||
base::Result<bool> isEthernet(const std::string& interface) {
|
|
||||||
int rv = hardwareAddressType(interface);
|
|
||||||
if (rv < 0) {
|
|
||||||
errno = -rv;
|
|
||||||
return ErrnoErrorf("Get hardware address type of interface {} failed", interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (rv) {
|
|
||||||
case ARPHRD_ETHER:
|
|
||||||
return true;
|
|
||||||
case ARPHRD_NONE:
|
|
||||||
case ARPHRD_RAWIP: // in Linux 4.14+ rmnet support was upstreamed and this is 519
|
|
||||||
case 530: // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
errno = EAFNOSUPPORT; // Address family not supported
|
|
||||||
return ErrnoErrorf("Unknown hardware address type {} on interface {}", rv, interface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: use //system/netd/server/NetlinkCommands.cpp:openNetlinkSocket(protocol)
|
|
||||||
// and //system/netd/server/SockDiag.cpp:checkError(fd)
|
|
||||||
static int sendAndProcessNetlinkResponse(const void* req, int len) {
|
|
||||||
base::unique_fd fd(socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE));
|
|
||||||
if (fd == -1) {
|
|
||||||
const int err = errno;
|
|
||||||
ALOGE("socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE)");
|
|
||||||
return -err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr int on = 1;
|
|
||||||
int rv = setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &on, sizeof(on));
|
|
||||||
if (rv) ALOGE("setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, %d)", on);
|
|
||||||
|
|
||||||
// this is needed to get sane strace netlink parsing, it allocates the pid
|
|
||||||
rv = bind(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR));
|
|
||||||
if (rv) {
|
|
||||||
const int err = errno;
|
|
||||||
ALOGE("bind(fd, {AF_NETLINK, 0, 0})");
|
|
||||||
return -err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we do not want to receive messages from anyone besides the kernel
|
|
||||||
rv = connect(fd, (const struct sockaddr*)&KERNEL_NLADDR, sizeof(KERNEL_NLADDR));
|
|
||||||
if (rv) {
|
|
||||||
const int err = errno;
|
|
||||||
ALOGE("connect(fd, {AF_NETLINK, 0, 0})");
|
|
||||||
return -err;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = send(fd, req, len, 0);
|
|
||||||
if (rv == -1) return -errno;
|
|
||||||
if (rv != len) return -EMSGSIZE;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
nlmsghdr h;
|
|
||||||
nlmsgerr e;
|
|
||||||
char buf[256];
|
|
||||||
} resp = {};
|
|
||||||
|
|
||||||
rv = recv(fd, &resp, sizeof(resp), MSG_TRUNC);
|
|
||||||
|
|
||||||
if (rv == -1) {
|
|
||||||
const int err = errno;
|
|
||||||
ALOGE("recv() failed");
|
|
||||||
return -err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rv < (int)NLMSG_SPACE(sizeof(struct nlmsgerr))) {
|
|
||||||
ALOGE("recv() returned short packet: %d", rv);
|
|
||||||
return -EMSGSIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resp.h.nlmsg_len != (unsigned)rv) {
|
|
||||||
ALOGE("recv() returned invalid header length: %d != %d", resp.h.nlmsg_len, rv);
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resp.h.nlmsg_type != NLMSG_ERROR) {
|
|
||||||
ALOGE("recv() did not return NLMSG_ERROR message: %d", resp.h.nlmsg_type);
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.e.error; // returns 0 on success
|
|
||||||
}
|
|
||||||
|
|
||||||
// ADD: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_EXCL|NLM_F_CREATE
|
|
||||||
// REPLACE: nlMsgType=RTM_NEWQDISC nlMsgFlags=NLM_F_CREATE|NLM_F_REPLACE
|
|
||||||
// DEL: nlMsgType=RTM_DELQDISC nlMsgFlags=0
|
|
||||||
int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags) {
|
|
||||||
// This is the name of the qdisc we are attaching.
|
|
||||||
// Some hoop jumping to make this compile time constant with known size,
|
|
||||||
// so that the structure declaration is well defined at compile time.
|
|
||||||
#define CLSACT "clsact"
|
|
||||||
// sizeof() includes the terminating NULL
|
|
||||||
static constexpr size_t ASCIIZ_LEN_CLSACT = sizeof(CLSACT);
|
|
||||||
|
|
||||||
const struct {
|
|
||||||
nlmsghdr n;
|
|
||||||
tcmsg t;
|
|
||||||
struct {
|
|
||||||
nlattr attr;
|
|
||||||
char str[NLMSG_ALIGN(ASCIIZ_LEN_CLSACT)];
|
|
||||||
} kind;
|
|
||||||
} req = {
|
|
||||||
.n =
|
|
||||||
{
|
|
||||||
.nlmsg_len = sizeof(req),
|
|
||||||
.nlmsg_type = nlMsgType,
|
|
||||||
.nlmsg_flags = static_cast<__u16>(NETLINK_REQUEST_FLAGS | nlMsgFlags),
|
|
||||||
},
|
|
||||||
.t =
|
|
||||||
{
|
|
||||||
.tcm_family = AF_UNSPEC,
|
|
||||||
.tcm_ifindex = ifIndex,
|
|
||||||
.tcm_handle = TC_H_MAKE(TC_H_CLSACT, 0),
|
|
||||||
.tcm_parent = TC_H_CLSACT,
|
|
||||||
},
|
|
||||||
.kind =
|
|
||||||
{
|
|
||||||
.attr =
|
|
||||||
{
|
|
||||||
.nla_len = NLA_HDRLEN + ASCIIZ_LEN_CLSACT,
|
|
||||||
.nla_type = TCA_KIND,
|
|
||||||
},
|
|
||||||
.str = CLSACT,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
#undef CLSACT
|
|
||||||
|
|
||||||
return sendAndProcessNetlinkResponse(&req, sizeof(req));
|
|
||||||
}
|
|
||||||
|
|
||||||
// tc filter add dev .. in/egress prio 4 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
|
|
||||||
// direct-action
|
|
||||||
int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t proto, int bpfFd, bool ethernet) {
|
|
||||||
// This is the name of the filter we're attaching (ie. this is the 'bpf'
|
|
||||||
// packet classifier enabled by kernel config option CONFIG_NET_CLS_BPF.
|
|
||||||
//
|
|
||||||
// We go through some hoops in order to make this compile time constants
|
|
||||||
// so that we can define the struct further down the function with the
|
|
||||||
// field for this sized correctly already during the build.
|
|
||||||
#define BPF "bpf"
|
|
||||||
// sizeof() includes the terminating NULL
|
|
||||||
static constexpr size_t ASCIIZ_LEN_BPF = sizeof(BPF);
|
|
||||||
|
|
||||||
// This is to replicate program name suffix used by 'tc' Linux cli
|
|
||||||
// when it attaches programs.
|
|
||||||
#define FSOBJ_SUFFIX ":[*fsobj]"
|
|
||||||
|
|
||||||
// This macro expands (from header files) to:
|
|
||||||
// prog_clatd_schedcls_ingress6_clat_rawip:[*fsobj]
|
|
||||||
// and is the name of the pinned ingress ebpf program for ARPHRD_RAWIP interfaces.
|
|
||||||
// (also compatible with anything that has 0 size L2 header)
|
|
||||||
static constexpr char name_clat_rx_rawip[] = CLAT_INGRESS6_PROG_RAWIP_NAME FSOBJ_SUFFIX;
|
|
||||||
|
|
||||||
// This macro expands (from header files) to:
|
|
||||||
// prog_clatd_schedcls_ingress6_clat_ether:[*fsobj]
|
|
||||||
// and is the name of the pinned ingress ebpf program for ARPHRD_ETHER interfaces.
|
|
||||||
// (also compatible with anything that has standard ethernet header)
|
|
||||||
static constexpr char name_clat_rx_ether[] = CLAT_INGRESS6_PROG_ETHER_NAME FSOBJ_SUFFIX;
|
|
||||||
|
|
||||||
// This macro expands (from header files) to:
|
|
||||||
// prog_clatd_schedcls_egress4_clat_rawip:[*fsobj]
|
|
||||||
// and is the name of the pinned egress ebpf program for ARPHRD_RAWIP interfaces.
|
|
||||||
// (also compatible with anything that has 0 size L2 header)
|
|
||||||
static constexpr char name_clat_tx_rawip[] = CLAT_EGRESS4_PROG_RAWIP_NAME FSOBJ_SUFFIX;
|
|
||||||
|
|
||||||
// This macro expands (from header files) to:
|
|
||||||
// prog_clatd_schedcls_egress4_clat_ether:[*fsobj]
|
|
||||||
// and is the name of the pinned egress ebpf program for ARPHRD_ETHER interfaces.
|
|
||||||
// (also compatible with anything that has standard ethernet header)
|
|
||||||
static constexpr char name_clat_tx_ether[] = CLAT_EGRESS4_PROG_ETHER_NAME FSOBJ_SUFFIX;
|
|
||||||
|
|
||||||
#undef FSOBJ_SUFFIX
|
|
||||||
|
|
||||||
// The actual name we'll use is determined at run time via 'ethernet' and 'ingress'
|
|
||||||
// booleans. We need to compile time allocate enough space in the struct
|
|
||||||
// hence this macro magic to make sure we have enough space for either
|
|
||||||
// possibility. In practice some of these are actually the same size.
|
|
||||||
static constexpr size_t ASCIIZ_MAXLEN_NAME = max({
|
|
||||||
sizeof(name_clat_rx_rawip),
|
|
||||||
sizeof(name_clat_rx_ether),
|
|
||||||
sizeof(name_clat_tx_rawip),
|
|
||||||
sizeof(name_clat_tx_ether),
|
|
||||||
});
|
|
||||||
|
|
||||||
// These are not compile time constants: 'name' is used in strncpy below
|
|
||||||
const char* const name_clat_rx = ethernet ? name_clat_rx_ether : name_clat_rx_rawip;
|
|
||||||
const char* const name_clat_tx = ethernet ? name_clat_tx_ether : name_clat_tx_rawip;
|
|
||||||
const char* const name = ingress ? name_clat_rx : name_clat_tx;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
nlmsghdr n;
|
|
||||||
tcmsg t;
|
|
||||||
struct {
|
|
||||||
nlattr attr;
|
|
||||||
char str[NLMSG_ALIGN(ASCIIZ_LEN_BPF)];
|
|
||||||
} kind;
|
|
||||||
struct {
|
|
||||||
nlattr attr;
|
|
||||||
struct {
|
|
||||||
nlattr attr;
|
|
||||||
__u32 u32;
|
|
||||||
} fd;
|
|
||||||
struct {
|
|
||||||
nlattr attr;
|
|
||||||
char str[NLMSG_ALIGN(ASCIIZ_MAXLEN_NAME)];
|
|
||||||
} name;
|
|
||||||
struct {
|
|
||||||
nlattr attr;
|
|
||||||
__u32 u32;
|
|
||||||
} flags;
|
|
||||||
} options;
|
|
||||||
} req = {
|
|
||||||
.n =
|
|
||||||
{
|
|
||||||
.nlmsg_len = sizeof(req),
|
|
||||||
.nlmsg_type = RTM_NEWTFILTER,
|
|
||||||
.nlmsg_flags = NETLINK_REQUEST_FLAGS | NLM_F_EXCL | NLM_F_CREATE,
|
|
||||||
},
|
|
||||||
.t =
|
|
||||||
{
|
|
||||||
.tcm_family = AF_UNSPEC,
|
|
||||||
.tcm_ifindex = ifIndex,
|
|
||||||
.tcm_handle = TC_H_UNSPEC,
|
|
||||||
.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
|
|
||||||
ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
|
|
||||||
.tcm_info = static_cast<__u32>((PRIO_CLAT << 16) | htons(proto)),
|
|
||||||
},
|
|
||||||
.kind =
|
|
||||||
{
|
|
||||||
.attr =
|
|
||||||
{
|
|
||||||
.nla_len = sizeof(req.kind),
|
|
||||||
.nla_type = TCA_KIND,
|
|
||||||
},
|
|
||||||
.str = BPF,
|
|
||||||
},
|
|
||||||
.options =
|
|
||||||
{
|
|
||||||
.attr =
|
|
||||||
{
|
|
||||||
.nla_len = sizeof(req.options),
|
|
||||||
.nla_type = NLA_F_NESTED | TCA_OPTIONS,
|
|
||||||
},
|
|
||||||
.fd =
|
|
||||||
{
|
|
||||||
.attr =
|
|
||||||
{
|
|
||||||
.nla_len = sizeof(req.options.fd),
|
|
||||||
.nla_type = TCA_BPF_FD,
|
|
||||||
},
|
|
||||||
.u32 = static_cast<__u32>(bpfFd),
|
|
||||||
},
|
|
||||||
.name =
|
|
||||||
{
|
|
||||||
.attr =
|
|
||||||
{
|
|
||||||
.nla_len = sizeof(req.options.name),
|
|
||||||
.nla_type = TCA_BPF_NAME,
|
|
||||||
},
|
|
||||||
// Visible via 'tc filter show', but
|
|
||||||
// is overwritten by strncpy below
|
|
||||||
.str = "placeholder",
|
|
||||||
},
|
|
||||||
.flags =
|
|
||||||
{
|
|
||||||
.attr =
|
|
||||||
{
|
|
||||||
.nla_len = sizeof(req.options.flags),
|
|
||||||
.nla_type = TCA_BPF_FLAGS,
|
|
||||||
},
|
|
||||||
.u32 = TCA_BPF_FLAG_ACT_DIRECT,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
#undef BPF
|
|
||||||
|
|
||||||
strncpy(req.options.name.str, name, sizeof(req.options.name.str));
|
|
||||||
|
|
||||||
return sendAndProcessNetlinkResponse(&req, sizeof(req));
|
|
||||||
}
|
|
||||||
|
|
||||||
// tc filter del dev .. in/egress prio 4 protocol ..
|
|
||||||
int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t proto) {
|
|
||||||
const struct {
|
|
||||||
nlmsghdr n;
|
|
||||||
tcmsg t;
|
|
||||||
} req = {
|
|
||||||
.n =
|
|
||||||
{
|
|
||||||
.nlmsg_len = sizeof(req),
|
|
||||||
.nlmsg_type = RTM_DELTFILTER,
|
|
||||||
.nlmsg_flags = NETLINK_REQUEST_FLAGS,
|
|
||||||
},
|
|
||||||
.t =
|
|
||||||
{
|
|
||||||
.tcm_family = AF_UNSPEC,
|
|
||||||
.tcm_ifindex = ifIndex,
|
|
||||||
.tcm_handle = TC_H_UNSPEC,
|
|
||||||
.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
|
|
||||||
ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS),
|
|
||||||
.tcm_info = (static_cast<uint32_t>(prio) << 16) |
|
|
||||||
static_cast<uint32_t>(htons(proto)),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return sendAndProcessNetlinkResponse(&req, sizeof(req));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace net
|
|
||||||
} // namespace android
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019 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.
|
|
||||||
*
|
|
||||||
* TcUtilsTest.cpp - unit tests for TcUtils.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "libclat/TcUtils.h"
|
|
||||||
|
|
||||||
#include <linux/if_arp.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
#include "bpf/BpfUtils.h"
|
|
||||||
#include "bpf_shared.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace net {
|
|
||||||
|
|
||||||
class TcUtilsTest : public ::testing::Test {
|
|
||||||
public:
|
|
||||||
void SetUp() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, HardwareAddressTypeOfNonExistingIf) {
|
|
||||||
ASSERT_EQ(-ENODEV, hardwareAddressType("not_existing_if"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, HardwareAddressTypeOfLoopback) {
|
|
||||||
ASSERT_EQ(ARPHRD_LOOPBACK, hardwareAddressType("lo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If wireless 'wlan0' interface exists it should be Ethernet.
|
|
||||||
TEST_F(TcUtilsTest, HardwareAddressTypeOfWireless) {
|
|
||||||
int type = hardwareAddressType("wlan0");
|
|
||||||
if (type == -ENODEV) return;
|
|
||||||
|
|
||||||
ASSERT_EQ(ARPHRD_ETHER, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If cellular 'rmnet_data0' interface exists it should
|
|
||||||
// *probably* not be Ethernet and instead be RawIp.
|
|
||||||
TEST_F(TcUtilsTest, HardwareAddressTypeOfCellular) {
|
|
||||||
int type = hardwareAddressType("rmnet_data0");
|
|
||||||
if (type == -ENODEV) return;
|
|
||||||
|
|
||||||
ASSERT_NE(ARPHRD_ETHER, type);
|
|
||||||
|
|
||||||
// ARPHRD_RAWIP is 530 on some pre-4.14 Qualcomm devices.
|
|
||||||
if (type == 530) return;
|
|
||||||
|
|
||||||
ASSERT_EQ(ARPHRD_RAWIP, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, IsEthernetOfNonExistingIf) {
|
|
||||||
auto res = isEthernet("not_existing_if");
|
|
||||||
ASSERT_FALSE(res.ok());
|
|
||||||
ASSERT_EQ(ENODEV, res.error().code());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, IsEthernetOfLoopback) {
|
|
||||||
auto res = isEthernet("lo");
|
|
||||||
ASSERT_FALSE(res.ok());
|
|
||||||
ASSERT_EQ(EAFNOSUPPORT, res.error().code());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If wireless 'wlan0' interface exists it should be Ethernet.
|
|
||||||
// See also HardwareAddressTypeOfWireless.
|
|
||||||
TEST_F(TcUtilsTest, IsEthernetOfWireless) {
|
|
||||||
auto res = isEthernet("wlan0");
|
|
||||||
if (!res.ok() && res.error().code() == ENODEV) return;
|
|
||||||
|
|
||||||
ASSERT_RESULT_OK(res);
|
|
||||||
ASSERT_TRUE(res.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If cellular 'rmnet_data0' interface exists it should
|
|
||||||
// *probably* not be Ethernet and instead be RawIp.
|
|
||||||
// See also HardwareAddressTypeOfCellular.
|
|
||||||
TEST_F(TcUtilsTest, IsEthernetOfCellular) {
|
|
||||||
auto res = isEthernet("rmnet_data0");
|
|
||||||
if (!res.ok() && res.error().code() == ENODEV) return;
|
|
||||||
|
|
||||||
ASSERT_RESULT_OK(res);
|
|
||||||
ASSERT_FALSE(res.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, DeviceMTUOfNonExistingIf) {
|
|
||||||
ASSERT_EQ(-ENODEV, deviceMTU("not_existing_if"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, DeviceMTUofLoopback) {
|
|
||||||
ASSERT_EQ(65536, deviceMTU("lo"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, GetClatEgress4MapFd) {
|
|
||||||
int fd = getClatEgress4MapFd();
|
|
||||||
ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
|
|
||||||
EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, GetClatEgress4RawIpProgFd) {
|
|
||||||
int fd = getClatEgress4ProgFd(RAWIP);
|
|
||||||
ASSERT_GE(fd, 3);
|
|
||||||
EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, GetClatEgress4EtherProgFd) {
|
|
||||||
int fd = getClatEgress4ProgFd(ETHER);
|
|
||||||
ASSERT_GE(fd, 3);
|
|
||||||
EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, GetClatIngress6MapFd) {
|
|
||||||
int fd = getClatIngress6MapFd();
|
|
||||||
ASSERT_GE(fd, 3); // 0,1,2 - stdin/out/err, thus fd >= 3
|
|
||||||
EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, GetClatIngress6RawIpProgFd) {
|
|
||||||
int fd = getClatIngress6ProgFd(RAWIP);
|
|
||||||
ASSERT_GE(fd, 3);
|
|
||||||
EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, GetClatIngress6EtherProgFd) {
|
|
||||||
int fd = getClatIngress6ProgFd(ETHER);
|
|
||||||
ASSERT_GE(fd, 3);
|
|
||||||
EXPECT_EQ(FD_CLOEXEC, fcntl(fd, F_GETFD));
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// See Linux kernel source in include/net/flow.h
|
|
||||||
#define LOOPBACK_IFINDEX 1
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, AttachReplaceDetachClsactLo) {
|
|
||||||
// This attaches and detaches a configuration-less and thus no-op clsact
|
|
||||||
// qdisc to loopback interface (and it takes fractions of a second)
|
|
||||||
EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(0, tcQdiscReplaceDevClsact(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(0, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(-EINVAL, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void checkAttachDetachBpfFilterClsactLo(const bool ingress, const bool ethernet) {
|
|
||||||
// Older kernels return EINVAL instead of ENOENT due to lacking proper error propagation...
|
|
||||||
const int errNOENT = android::bpf::isAtLeastKernelVersion(4, 19, 0) ? ENOENT : EINVAL;
|
|
||||||
|
|
||||||
int clatBpfFd = ingress ? getClatIngress6ProgFd(ethernet) : getClatEgress4ProgFd(ethernet);
|
|
||||||
ASSERT_GE(clatBpfFd, 3);
|
|
||||||
|
|
||||||
// This attaches and detaches a clsact plus ebpf program to loopback
|
|
||||||
// interface, but it should not affect traffic by virtue of us not
|
|
||||||
// actually populating the ebpf control map.
|
|
||||||
// Furthermore: it only takes fractions of a second.
|
|
||||||
EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(0, tcQdiscAddDevClsact(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(-errNOENT, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
|
|
||||||
if (ingress) {
|
|
||||||
EXPECT_EQ(0, tcFilterAddDevIngressClatIpv6(LOOPBACK_IFINDEX, clatBpfFd, ethernet));
|
|
||||||
EXPECT_EQ(0, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(0, tcFilterAddDevEgressClatIpv4(LOOPBACK_IFINDEX, clatBpfFd, ethernet));
|
|
||||||
EXPECT_EQ(0, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
|
|
||||||
}
|
|
||||||
EXPECT_EQ(-errNOENT, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(-errNOENT, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(0, tcQdiscDelDevClsact(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(-EINVAL, tcFilterDelDevIngressClatIpv6(LOOPBACK_IFINDEX));
|
|
||||||
EXPECT_EQ(-EINVAL, tcFilterDelDevEgressClatIpv4(LOOPBACK_IFINDEX));
|
|
||||||
|
|
||||||
close(clatBpfFd);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, CheckAttachBpfFilterRawIpClsactEgressLo) {
|
|
||||||
checkAttachDetachBpfFilterClsactLo(EGRESS, RAWIP);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, CheckAttachBpfFilterEthernetClsactEgressLo) {
|
|
||||||
checkAttachDetachBpfFilterClsactLo(EGRESS, ETHER);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, CheckAttachBpfFilterRawIpClsactIngressLo) {
|
|
||||||
checkAttachDetachBpfFilterClsactLo(INGRESS, RAWIP);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TcUtilsTest, CheckAttachBpfFilterEthernetClsactIngressLo) {
|
|
||||||
checkAttachDetachBpfFilterClsactLo(INGRESS, ETHER);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace net
|
|
||||||
} // namespace android
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2021 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.
|
|
||||||
*
|
|
||||||
* main.c - main function
|
|
||||||
*/
|
|
||||||
#define LOG_TAG "bpfhelper"
|
|
||||||
|
|
||||||
#include "libclat/bpfhelper.h"
|
|
||||||
|
|
||||||
#include <android-base/unique_fd.h>
|
|
||||||
#include <log/log.h>
|
|
||||||
|
|
||||||
#include "bpf/BpfMap.h"
|
|
||||||
#include "libclat/TcUtils.h"
|
|
||||||
|
|
||||||
#define DEVICEPREFIX "v4-"
|
|
||||||
|
|
||||||
using android::base::unique_fd;
|
|
||||||
using android::bpf::BpfMap;
|
|
||||||
|
|
||||||
BpfMap<ClatEgress4Key, ClatEgress4Value> mClatEgress4Map;
|
|
||||||
BpfMap<ClatIngress6Key, ClatIngress6Value> mClatIngress6Map;
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace net {
|
|
||||||
namespace clat {
|
|
||||||
|
|
||||||
// TODO: have a clearMap function to remove all stubs while system server crash.
|
|
||||||
// For long term, move bpf access into java and map initialization should live
|
|
||||||
// ClatCoordinator constructor.
|
|
||||||
int initMaps(void) {
|
|
||||||
int rv = getClatEgress4MapFd();
|
|
||||||
if (rv < 0) {
|
|
||||||
ALOGE("getClatEgress4MapFd() failure: %s", strerror(-rv));
|
|
||||||
return -rv;
|
|
||||||
}
|
|
||||||
mClatEgress4Map.reset(rv);
|
|
||||||
|
|
||||||
rv = getClatIngress6MapFd();
|
|
||||||
if (rv < 0) {
|
|
||||||
ALOGE("getClatIngress6MapFd() failure: %s", strerror(-rv));
|
|
||||||
mClatEgress4Map.reset(-1);
|
|
||||||
return -rv;
|
|
||||||
}
|
|
||||||
mClatIngress6Map.reset(rv);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void maybeStartBpf(const ClatdTracker& tracker) {
|
|
||||||
auto isEthernet = android::net::isEthernet(tracker.iface);
|
|
||||||
if (!isEthernet.ok()) {
|
|
||||||
ALOGE("isEthernet(%s[%d]) failure: %s", tracker.iface, tracker.ifIndex,
|
|
||||||
isEthernet.error().message().c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This program will be attached to the v4-* interface which is a TUN and thus always rawip.
|
|
||||||
int rv = getClatEgress4ProgFd(RAWIP);
|
|
||||||
if (rv < 0) {
|
|
||||||
ALOGE("getClatEgress4ProgFd(RAWIP) failure: %s", strerror(-rv));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unique_fd txRawIpProgFd(rv);
|
|
||||||
|
|
||||||
rv = getClatIngress6ProgFd(isEthernet.value());
|
|
||||||
if (rv < 0) {
|
|
||||||
ALOGE("getClatIngress6ProgFd(%d) failure: %s", isEthernet.value(), strerror(-rv));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unique_fd rxProgFd(rv);
|
|
||||||
|
|
||||||
// We do tc setup *after* populating the maps, so scanning through them
|
|
||||||
// can always be used to tell us what needs cleanup.
|
|
||||||
|
|
||||||
// Usually the clsact will be added in RouteController::addInterfaceToPhysicalNetwork.
|
|
||||||
// But clat is started before the v4- interface is added to the network. The clat startup have
|
|
||||||
// to add clsact of v4- tun interface first for adding bpf filter in maybeStartBpf.
|
|
||||||
// TODO: move "qdisc add clsact" of v4- tun interface out from ClatdController.
|
|
||||||
rv = tcQdiscAddDevClsact(tracker.v4ifIndex);
|
|
||||||
if (rv) {
|
|
||||||
ALOGE("tcQdiscAddDevClsact(%d[%s]) failure: %s", tracker.v4ifIndex, tracker.v4iface,
|
|
||||||
strerror(-rv));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = tcFilterAddDevEgressClatIpv4(tracker.v4ifIndex, txRawIpProgFd, RAWIP);
|
|
||||||
if (rv) {
|
|
||||||
ALOGE("tcFilterAddDevEgressClatIpv4(%d[%s], RAWIP) failure: %s", tracker.v4ifIndex,
|
|
||||||
tracker.v4iface, strerror(-rv));
|
|
||||||
|
|
||||||
// The v4- interface clsact is not deleted for unwinding error because once it is created
|
|
||||||
// with interface addition, the lifetime is till interface deletion. Moreover, the clsact
|
|
||||||
// has no clat filter now. It should not break anything.
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = tcFilterAddDevIngressClatIpv6(tracker.ifIndex, rxProgFd, isEthernet.value());
|
|
||||||
if (rv) {
|
|
||||||
ALOGE("tcFilterAddDevIngressClatIpv6(%d[%s], %d) failure: %s", tracker.ifIndex,
|
|
||||||
tracker.iface, isEthernet.value(), strerror(-rv));
|
|
||||||
rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex);
|
|
||||||
if (rv) {
|
|
||||||
ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex,
|
|
||||||
tracker.v4iface, strerror(-rv));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The v4- interface clsact is not deleted. See the reason in the error unwinding code of
|
|
||||||
// the egress filter attaching of v4- tun interface.
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// success
|
|
||||||
}
|
|
||||||
|
|
||||||
void maybeStopBpf(const ClatdTracker& tracker) {
|
|
||||||
int rv = tcFilterDelDevIngressClatIpv6(tracker.ifIndex);
|
|
||||||
if (rv < 0) {
|
|
||||||
ALOGE("tcFilterDelDevIngressClatIpv6(%d[%s]) failure: %s", tracker.ifIndex, tracker.iface,
|
|
||||||
strerror(-rv));
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = tcFilterDelDevEgressClatIpv4(tracker.v4ifIndex);
|
|
||||||
if (rv < 0) {
|
|
||||||
ALOGE("tcFilterDelDevEgressClatIpv4(%d[%s]) failure: %s", tracker.v4ifIndex,
|
|
||||||
tracker.v4iface, strerror(-rv));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace clat
|
|
||||||
} // namespace net
|
|
||||||
} // namespace android
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2019 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <android-base/result.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <linux/if_ether.h>
|
|
||||||
#include <linux/if_link.h>
|
|
||||||
#include <linux/rtnetlink.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "bpf/BpfUtils.h"
|
|
||||||
#include "bpf_shared.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace net {
|
|
||||||
|
|
||||||
// For better code clarity - do not change values - used for booleans like
|
|
||||||
// with_ethernet_header or isEthernet.
|
|
||||||
constexpr bool RAWIP = false;
|
|
||||||
constexpr bool ETHER = true;
|
|
||||||
|
|
||||||
// For better code clarity when used for 'bool ingress' parameter.
|
|
||||||
constexpr bool EGRESS = false;
|
|
||||||
constexpr bool INGRESS = true;
|
|
||||||
|
|
||||||
// The priority of clat hook - must be after tethering.
|
|
||||||
constexpr uint16_t PRIO_CLAT = 4;
|
|
||||||
|
|
||||||
// this returns an ARPHRD_* constant or a -errno
|
|
||||||
int hardwareAddressType(const std::string& interface);
|
|
||||||
|
|
||||||
// return MTU or -errno
|
|
||||||
int deviceMTU(const std::string& interface);
|
|
||||||
|
|
||||||
base::Result<bool> isEthernet(const std::string& interface);
|
|
||||||
|
|
||||||
inline int getClatEgress4MapFd(void) {
|
|
||||||
const int fd = bpf::mapRetrieveRW(CLAT_EGRESS4_MAP_PATH);
|
|
||||||
return (fd == -1) ? -errno : fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int getClatEgress4ProgFd(bool with_ethernet_header) {
|
|
||||||
const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_EGRESS4_PROG_ETHER_PATH
|
|
||||||
: CLAT_EGRESS4_PROG_RAWIP_PATH);
|
|
||||||
return (fd == -1) ? -errno : fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int getClatIngress6MapFd(void) {
|
|
||||||
const int fd = bpf::mapRetrieveRW(CLAT_INGRESS6_MAP_PATH);
|
|
||||||
return (fd == -1) ? -errno : fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int getClatIngress6ProgFd(bool with_ethernet_header) {
|
|
||||||
const int fd = bpf::retrieveProgram(with_ethernet_header ? CLAT_INGRESS6_PROG_ETHER_PATH
|
|
||||||
: CLAT_INGRESS6_PROG_RAWIP_PATH);
|
|
||||||
return (fd == -1) ? -errno : fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
int doTcQdiscClsact(int ifIndex, uint16_t nlMsgType, uint16_t nlMsgFlags);
|
|
||||||
|
|
||||||
inline int tcQdiscAddDevClsact(int ifIndex) {
|
|
||||||
return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_EXCL | NLM_F_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int tcQdiscReplaceDevClsact(int ifIndex) {
|
|
||||||
return doTcQdiscClsact(ifIndex, RTM_NEWQDISC, NLM_F_CREATE | NLM_F_REPLACE);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int tcQdiscDelDevClsact(int ifIndex) {
|
|
||||||
return doTcQdiscClsact(ifIndex, RTM_DELQDISC, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tc filter add dev .. in/egress prio 4 protocol ipv6/ip bpf object-pinned /sys/fs/bpf/...
|
|
||||||
// direct-action
|
|
||||||
int tcFilterAddDevBpf(int ifIndex, bool ingress, uint16_t proto, int bpfFd, bool ethernet);
|
|
||||||
|
|
||||||
// tc filter add dev .. ingress prio 4 protocol ipv6 bpf object-pinned /sys/fs/bpf/... direct-action
|
|
||||||
inline int tcFilterAddDevIngressClatIpv6(int ifIndex, int bpfFd, bool ethernet) {
|
|
||||||
return tcFilterAddDevBpf(ifIndex, INGRESS, ETH_P_IPV6, bpfFd, ethernet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tc filter add dev .. egress prio 4 protocol ip bpf object-pinned /sys/fs/bpf/... direct-action
|
|
||||||
inline int tcFilterAddDevEgressClatIpv4(int ifIndex, int bpfFd, bool ethernet) {
|
|
||||||
return tcFilterAddDevBpf(ifIndex, EGRESS, ETH_P_IP, bpfFd, ethernet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tc filter del dev .. in/egress prio .. protocol ..
|
|
||||||
int tcFilterDelDev(int ifIndex, bool ingress, uint16_t prio, uint16_t proto);
|
|
||||||
|
|
||||||
// tc filter del dev .. ingress prio 4 protocol ipv6
|
|
||||||
inline int tcFilterDelDevIngressClatIpv6(int ifIndex) {
|
|
||||||
return tcFilterDelDev(ifIndex, INGRESS, PRIO_CLAT, ETH_P_IPV6);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tc filter del dev .. egress prio 4 protocol ip
|
|
||||||
inline int tcFilterDelDevEgressClatIpv4(int ifIndex) {
|
|
||||||
return tcFilterDelDev(ifIndex, EGRESS, PRIO_CLAT, ETH_P_IP);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace net
|
|
||||||
} // namespace android
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
// Copyright (C) 2021 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.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <linux/if.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
namespace net {
|
|
||||||
namespace clat {
|
|
||||||
|
|
||||||
struct ClatdTracker {
|
|
||||||
unsigned ifIndex;
|
|
||||||
char iface[IFNAMSIZ];
|
|
||||||
unsigned v4ifIndex;
|
|
||||||
char v4iface[IFNAMSIZ];
|
|
||||||
in_addr v4;
|
|
||||||
in6_addr v6;
|
|
||||||
in6_addr pfx96;
|
|
||||||
};
|
|
||||||
|
|
||||||
int initMaps(void);
|
|
||||||
void maybeStartBpf(const ClatdTracker& tracker);
|
|
||||||
void maybeStopBpf(const ClatdTracker& tracker);
|
|
||||||
|
|
||||||
} // namespace clat
|
|
||||||
} // namespace net
|
|
||||||
} // namespace android
|
|
||||||
@@ -686,6 +686,23 @@ public class ClatCoordinator {
|
|||||||
private void maybeStopBpf(final ClatdTracker tracker) {
|
private void maybeStopBpf(final ClatdTracker tracker) {
|
||||||
if (mIngressMap == null || mEgressMap == null) return;
|
if (mIngressMap == null || mEgressMap == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
mDeps.tcFilterDelDev(tracker.ifIndex, INGRESS, (short) PRIO_CLAT, (short) ETH_P_IPV6);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "tc filter del dev (" + tracker.ifIndex + "[" + tracker.iface
|
||||||
|
+ "]) ingress prio PRIO_CLAT protocol ipv6 failure: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mDeps.tcFilterDelDev(tracker.v4ifIndex, EGRESS, (short) PRIO_CLAT, (short) ETH_P_IP);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "tc filter del dev (" + tracker.v4ifIndex + "[" + tracker.v4iface
|
||||||
|
+ "]) egress prio PRIO_CLAT protocol ip failure: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We cleanup the maps last, so scanning through them can be used to
|
||||||
|
// determine what still needs cleanup.
|
||||||
|
|
||||||
final ClatEgress4Key txKey = new ClatEgress4Key(tracker.v4ifIndex, tracker.v4);
|
final ClatEgress4Key txKey = new ClatEgress4Key(tracker.v4ifIndex, tracker.v4);
|
||||||
try {
|
try {
|
||||||
mEgressMap.deleteEntry(txKey);
|
mEgressMap.deleteEntry(txKey);
|
||||||
@@ -700,8 +717,6 @@ public class ClatCoordinator {
|
|||||||
} catch (ErrnoException | IllegalStateException e) {
|
} catch (ErrnoException | IllegalStateException e) {
|
||||||
Log.e(TAG, "Could not delete entry (" + rxKey + "): " + e);
|
Log.e(TAG, "Could not delete entry (" + rxKey + "): " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: dettach program.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -463,6 +463,10 @@ public class ClatCoordinatorTest {
|
|||||||
|
|
||||||
// [3] Expect clatd to stop successfully.
|
// [3] Expect clatd to stop successfully.
|
||||||
coordinator.clatStop();
|
coordinator.clatStop();
|
||||||
|
inOrder.verify(mDeps).tcFilterDelDev(eq(BASE_IFINDEX), eq(INGRESS),
|
||||||
|
eq((short) PRIO_CLAT), eq((short) ETH_P_IPV6));
|
||||||
|
inOrder.verify(mDeps).tcFilterDelDev(eq(STACKED_IFINDEX), eq(EGRESS),
|
||||||
|
eq((short) PRIO_CLAT), eq((short) ETH_P_IP));
|
||||||
inOrder.verify(mEgressMap).deleteEntry(eq(EGRESS_KEY));
|
inOrder.verify(mEgressMap).deleteEntry(eq(EGRESS_KEY));
|
||||||
inOrder.verify(mIngressMap).deleteEntry(eq(INGRESS_KEY));
|
inOrder.verify(mIngressMap).deleteEntry(eq(INGRESS_KEY));
|
||||||
inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
|
inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
|
||||||
|
|||||||
Reference in New Issue
Block a user