Merge "Remove NativeDaemonConnector relevant files"
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index dc646c3..8971f6b 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -37,21 +37,33 @@
// From kernel:include/net/ip.h
#define IP_DF 0x4000 // Flag: "Don't Fragment"
+// Used for iptables drops ingress clat packet. Beware of clat mark change may break the device
+// which is using the old clat mark in netd platform code. The reason is that the clat mark is a
+// mainline constant since T+ but netd iptable rules (ex: bandwidth control, firewall, and so on)
+// are set in stone.
+#define CLAT_MARK 0xdeadc1a7
+
DEFINE_BPF_MAP_GRW(clat_ingress6_map, HASH, ClatIngress6Key, ClatIngress6Value, 16, AID_SYSTEM)
static inline __always_inline int nat64(struct __sk_buff* skb, bool is_ethernet) {
- const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
- void* data = (void*)(long)skb->data;
- const void* data_end = (void*)(long)skb->data_end;
- const struct ethhdr* const eth = is_ethernet ? data : NULL; // used iff is_ethernet
- const struct ipv6hdr* const ip6 = is_ethernet ? (void*)(eth + 1) : data;
-
// Require ethernet dst mac address to be our unicast address.
if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_PIPE;
// Must be meta-ethernet IPv6 frame
if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_PIPE;
+ const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
+
+ // Not clear if this is actually necessary considering we use DPA (Direct Packet Access),
+ // but we need to make sure we can read the IPv6 header reliably so that we can set
+ // skb->mark = 0xDeadC1a7 for packets we fail to offload.
+ try_make_readable(skb, l2_header_size + sizeof(struct ipv6hdr));
+
+ void* data = (void*)(long)skb->data;
+ const void* data_end = (void*)(long)skb->data_end;
+ const struct ethhdr* const eth = is_ethernet ? data : NULL; // used iff is_ethernet
+ const struct ipv6hdr* const ip6 = is_ethernet ? (void*)(eth + 1) : data;
+
// Must have (ethernet and) ipv6 header
if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_PIPE;
@@ -64,17 +76,6 @@
// Maximum IPv6 payload length that can be translated to IPv4
if (ntohs(ip6->payload_len) > 0xFFFF - sizeof(struct iphdr)) return TC_ACT_PIPE;
- switch (ip6->nexthdr) {
- case IPPROTO_TCP: // For TCP & UDP the checksum neutrality of the chosen IPv6
- case IPPROTO_UDP: // address means there is no need to update their checksums.
- case IPPROTO_GRE: // We do not need to bother looking at GRE/ESP headers,
- case IPPROTO_ESP: // since there is never a checksum to update.
- break;
-
- default: // do not know how to handle anything else
- return TC_ACT_PIPE;
- }
-
ClatIngress6Key k = {
.iif = skb->ifindex,
.pfx96.in6_u.u6_addr32 =
@@ -90,6 +91,21 @@
if (!v) return TC_ACT_PIPE;
+ switch (ip6->nexthdr) {
+ case IPPROTO_TCP: // For TCP & UDP the checksum neutrality of the chosen IPv6
+ case IPPROTO_UDP: // address means there is no need to update their checksums.
+ case IPPROTO_GRE: // We do not need to bother looking at GRE/ESP headers,
+ case IPPROTO_ESP: // since there is never a checksum to update.
+ break;
+
+ default: // do not know how to handle anything else
+ // Mark ingress non-offloaded clat packet for dropping in ip6tables bw_raw_PREROUTING.
+ // Non-offloaded clat packet is going to be handled by clat daemon and ip6tables. The
+ // duplicate one in ip6tables is not necessary.
+ skb->mark = CLAT_MARK;
+ return TC_ACT_PIPE;
+ }
+
struct ethhdr eth2; // used iff is_ethernet
if (is_ethernet) {
eth2 = *eth; // Copy over the ethernet header (src/dst mac)
@@ -132,7 +148,13 @@
// Packet mutations begin - point of no return, but if this first modification fails
// the packet is probably still pristine, so let clatd handle it.
- if (bpf_skb_change_proto(skb, htons(ETH_P_IP), 0)) return TC_ACT_PIPE;
+ if (bpf_skb_change_proto(skb, htons(ETH_P_IP), 0)) {
+ // Mark ingress non-offloaded clat packet for dropping in ip6tables bw_raw_PREROUTING.
+ // Non-offloaded clat packet is going to be handled by clat daemon and ip6tables. The
+ // duplicate one in ip6tables is not necessary.
+ skb->mark = CLAT_MARK;
+ return TC_ACT_PIPE;
+ }
// This takes care of updating the skb->csum field for a CHECKSUM_COMPLETE packet.
//
@@ -198,13 +220,16 @@
DEFINE_BPF_PROG("schedcls/egress4/clat_rawip", AID_ROOT, AID_SYSTEM, sched_cls_egress4_clat_rawip)
(struct __sk_buff* skb) {
+ // Must be meta-ethernet IPv4 frame
+ if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_PIPE;
+
+ // Possibly not needed, but for consistency with nat64 up above
+ try_make_readable(skb, sizeof(struct iphdr));
+
void* data = (void*)(long)skb->data;
const void* data_end = (void*)(long)skb->data_end;
const struct iphdr* const ip4 = data;
- // Must be meta-ethernet IPv4 frame
- if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_PIPE;
-
// Must have ipv4 header
if (data + sizeof(*ip4) > data_end) return TC_ACT_PIPE;
diff --git a/framework-t/Sources.bp b/framework-t/Sources.bp
index 53b4163..b30ee80 100644
--- a/framework-t/Sources.bp
+++ b/framework-t/Sources.bp
@@ -124,13 +124,6 @@
],
}
-// TODO: remove this empty filegroup.
-filegroup {
- name: "framework-connectivity-tiramisu-sources",
- srcs: [],
- visibility: ["//frameworks/base"],
-}
-
filegroup {
name: "framework-connectivity-tiramisu-updatable-sources",
srcs: [
diff --git a/service-t/Sources.bp b/service-t/Sources.bp
index 4e669b6..187eadf 100644
--- a/service-t/Sources.bp
+++ b/service-t/Sources.bp
@@ -14,27 +14,6 @@
// limitations under the License.
//
-// NetworkStats related libraries.
-
-filegroup {
- name: "services.connectivity-netstats-sources",
- srcs: [
- "src/com/android/server/net/NetworkIdentity*.java",
- "src/com/android/server/net/NetworkStats*.java",
- "src/com/android/server/net/BpfInterfaceMapUpdater.java",
- "src/com/android/server/net/InterfaceMapValue.java",
- "src/com/android/server/net/CookieTagMapKey.java",
- "src/com/android/server/net/CookieTagMapValue.java",
- "src/com/android/server/net/StatsMapKey.java",
- "src/com/android/server/net/StatsMapValue.java",
- "src/com/android/server/net/UidStatsMapKey.java",
- ],
- path: "src",
- visibility: [
- "//visibility:private",
- ],
-}
-
// For test code only.
filegroup {
name: "lib_networkStatsFactory_native",
@@ -59,37 +38,3 @@
],
}
-// Connectivity-T common libraries.
-
-// TODO: remove this empty filegroup.
-filegroup {
- name: "services.connectivity-tiramisu-sources",
- srcs: [],
- path: "src",
- visibility: ["//frameworks/base/services/core"],
-}
-
-cc_library_shared {
- name: "libcom_android_net_module_util_jni",
- min_sdk_version: "30",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- "-Wthread-safety",
- ],
- srcs: [
- "jni/onload.cpp",
- ],
- stl: "libc++_static",
- static_libs: [
- "libnet_utils_device_common_bpfjni",
- ],
- shared_libs: [
- "liblog",
- "libnativehelper",
- ],
- apex_available: [
- "//apex_available:platform",
- ],
-}
diff --git a/service-t/jni/onload.cpp b/service-t/jni/onload.cpp
deleted file mode 100644
index bca4697..0000000
--- a/service-t/jni/onload.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2022 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.
- */
-
-#include <nativehelper/JNIHelp.h>
-#include <log/log.h>
-
-namespace android {
-
-int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name);
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
- JNIEnv *env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- ALOGE("GetEnv failed");
- return JNI_ERR;
- }
-
- if (register_com_android_net_module_util_BpfMap(env,
- "com/android/net/module/util/BpfMap") < 0) return JNI_ERR;
-
- return JNI_VERSION_1_6;
-}
-
-};
-
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index c291b3f..693d91a 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -29,8 +29,8 @@
import android.net.ConnectivityResources;
import android.net.EthernetManager;
import android.net.IEthernetServiceListener;
-import android.net.INetworkInterfaceOutcomeReceiver;
import android.net.INetd;
+import android.net.INetworkInterfaceOutcomeReceiver;
import android.net.ITetheredInterfaceCallback;
import android.net.InterfaceConfigurationParcel;
import android.net.IpConfiguration;
@@ -57,6 +57,7 @@
import java.io.FileDescriptor;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@@ -389,10 +390,33 @@
mHandler.post(() -> {
mIncludeTestInterfaces = include;
updateIfaceMatchRegexp();
+ if (!include) {
+ removeTestData();
+ }
mHandler.post(() -> trackAvailableInterfaces());
});
}
+ private void removeTestData() {
+ removeTestIpData();
+ removeTestCapabilityData();
+ }
+
+ private void removeTestIpData() {
+ final Iterator<String> iterator = mIpConfigurations.keySet().iterator();
+ while (iterator.hasNext()) {
+ final String iface = iterator.next();
+ if (iface.matches(TEST_IFACE_REGEXP)) {
+ mConfigStore.write(iface, null);
+ iterator.remove();
+ }
+ }
+ }
+
+ private void removeTestCapabilityData() {
+ mNetworkCapabilities.keySet().removeIf(iface -> iface.matches(TEST_IFACE_REGEXP));
+ }
+
public void requestTetheredInterface(ITetheredInterfaceCallback callback) {
mHandler.post(() -> {
if (!mTetheredInterfaceRequests.register(callback)) {
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 4517b5c..500c696 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -34,7 +34,6 @@
#include <netjniutils/netjniutils.h>
#include <private/android_filesystem_config.h>
-#include "libclat/bpfhelper.h"
#include "libclat/clatutils.h"
#include "nativehelper/scoped_utf_chars.h"
@@ -257,46 +256,6 @@
}
}
-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(
JNIEnv* env, jobject clazz, jobject tunJavaFd, jobject readSockJavaFd,
jobject writeSockJavaFd, jstring iface, jstring pfx96, jstring v4, jstring v6) {
@@ -404,15 +363,6 @@
posix_spawnattr_destroy(&attr);
posix_spawn_file_actions_destroy(&fa);
- // 6. Start BPF if any
- 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::maybeStartBpf(tracker);
- }
- }
-
return pid;
}
@@ -467,14 +417,6 @@
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);
}
diff --git a/service/native/libs/libclat/Android.bp b/service/native/libs/libclat/Android.bp
index 17ee996..68e4dc4 100644
--- a/service/native/libs/libclat/Android.bp
+++ b/service/native/libs/libclat/Android.bp
@@ -19,19 +19,12 @@
cc_library_static {
name: "libclat",
defaults: ["netd_defaults"],
- header_libs: [
- "bpf_connectivity_headers",
- "libbase_headers",
- ],
srcs: [
- "TcUtils.cpp", // TODO: move to frameworks/libs/net
- "bpfhelper.cpp",
"clatutils.cpp",
],
stl: "libc++_static",
static_libs: [
"libip_checksum",
- "libnetdutils", // for netdutils/UidConstants.h in bpf_shared.h
],
shared_libs: ["liblog"],
export_include_dirs: ["include"],
@@ -43,11 +36,7 @@
name: "libclat_test",
defaults: ["netd_defaults"],
test_suites: ["device-tests"],
- header_libs: [
- "bpf_connectivity_headers",
- ],
srcs: [
- "TcUtilsTest.cpp",
"clatutils_test.cpp",
],
static_libs: [
@@ -55,8 +44,6 @@
"libclat",
"libip_checksum",
"libnetd_test_tun_interface",
- "libnetdutils", // for netdutils/UidConstants.h in bpf_shared.h
- "libtcutils",
],
shared_libs: [
"liblog",
diff --git a/service/native/libs/libclat/TcUtils.cpp b/service/native/libs/libclat/TcUtils.cpp
deleted file mode 100644
index cdfb763..0000000
--- a/service/native/libs/libclat/TcUtils.cpp
+++ /dev/null
@@ -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
diff --git a/service/native/libs/libclat/TcUtilsTest.cpp b/service/native/libs/libclat/TcUtilsTest.cpp
deleted file mode 100644
index 08f3042..0000000
--- a/service/native/libs/libclat/TcUtilsTest.cpp
+++ /dev/null
@@ -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
diff --git a/service/native/libs/libclat/bpfhelper.cpp b/service/native/libs/libclat/bpfhelper.cpp
deleted file mode 100644
index 00785ad..0000000
--- a/service/native/libs/libclat/bpfhelper.cpp
+++ /dev/null
@@ -1,221 +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);
-
- ClatEgress4Key txKey = {
- .iif = tracker.v4ifIndex,
- .local4 = tracker.v4,
- };
- ClatEgress4Value txValue = {
- .oif = tracker.ifIndex,
- .local6 = tracker.v6,
- .pfx96 = tracker.pfx96,
- .oifIsEthernet = isEthernet.value(),
- };
-
- auto ret = mClatEgress4Map.writeValue(txKey, txValue, BPF_ANY);
- if (!ret.ok()) {
- ALOGE("mClatEgress4Map.writeValue failure: %s", strerror(ret.error().code()));
- return;
- }
-
- ClatIngress6Key rxKey = {
- .iif = tracker.ifIndex,
- .pfx96 = tracker.pfx96,
- .local6 = tracker.v6,
- };
- ClatIngress6Value rxValue = {
- // TODO: move all the clat code to eBPF and remove the tun interface entirely.
- .oif = tracker.v4ifIndex,
- .local4 = tracker.v4,
- };
-
- ret = mClatIngress6Map.writeValue(rxKey, rxValue, BPF_ANY);
- if (!ret.ok()) {
- ALOGE("mClatIngress6Map.writeValue failure: %s", strerror(ret.error().code()));
- ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok())
- ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
- return;
- }
-
- // 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));
- ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok())
- ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
- ret = mClatIngress6Map.deleteValue(rxKey);
- if (!ret.ok())
- ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
- 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.
-
- ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok())
- ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
- ret = mClatIngress6Map.deleteValue(rxKey);
- if (!ret.ok())
- ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
- 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.
-
- ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok())
- ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
- ret = mClatIngress6Map.deleteValue(rxKey);
- if (!ret.ok())
- ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
- 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));
- }
-
- // We cleanup the maps last, so scanning through them can be used to
- // determine what still needs cleanup.
-
- ClatEgress4Key txKey = {
- .iif = tracker.v4ifIndex,
- .local4 = tracker.v4,
- };
-
- auto ret = mClatEgress4Map.deleteValue(txKey);
- if (!ret.ok()) ALOGE("mClatEgress4Map.deleteValue failure: %s", strerror(ret.error().code()));
-
- ClatIngress6Key rxKey = {
- .iif = tracker.ifIndex,
- .pfx96 = tracker.pfx96,
- .local6 = tracker.v6,
- };
-
- ret = mClatIngress6Map.deleteValue(rxKey);
- if (!ret.ok()) ALOGE("mClatIngress6Map.deleteValue failure: %s", strerror(ret.error().code()));
-}
-
-} // namespace clat
-} // namespace net
-} // namespace android
diff --git a/service/native/libs/libclat/include/libclat/TcUtils.h b/service/native/libs/libclat/include/libclat/TcUtils.h
deleted file mode 100644
index 212838e..0000000
--- a/service/native/libs/libclat/include/libclat/TcUtils.h
+++ /dev/null
@@ -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
diff --git a/service/native/libs/libclat/include/libclat/bpfhelper.h b/service/native/libs/libclat/include/libclat/bpfhelper.h
deleted file mode 100644
index c0328c0..0000000
--- a/service/native/libs/libclat/include/libclat/bpfhelper.h
+++ /dev/null
@@ -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
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 2e26ae4..8aa5990 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -18,6 +18,8 @@
import static android.net.INetd.IF_STATE_UP;
import static android.net.INetd.PERMISSION_SYSTEM;
+import static android.system.OsConstants.ETH_P_IP;
+import static android.system.OsConstants.ETH_P_IPV6;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
@@ -30,10 +32,19 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import android.system.ErrnoException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.BpfMap;
+import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.InterfaceParams;
+import com.android.net.module.util.TcUtils;
+import com.android.net.module.util.bpf.ClatEgress4Key;
+import com.android.net.module.util.bpf.ClatEgress4Value;
+import com.android.net.module.util.bpf.ClatIngress6Key;
+import com.android.net.module.util.bpf.ClatIngress6Value;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -71,11 +82,44 @@
private static final int INVALID_IFINDEX = 0;
+ // For better code clarity when used for 'bool ingress' parameter.
+ @VisibleForTesting
+ static final boolean EGRESS = false;
+ @VisibleForTesting
+ static final boolean INGRESS = true;
+
+ // For better code clarity when used for 'bool ether' parameter.
+ static final boolean RAWIP = false;
+ static final boolean ETHER = true;
+
+ // The priority of clat hook - must be after tethering.
+ @VisibleForTesting
+ static final int PRIO_CLAT = 4;
+
+ private static final String CLAT_EGRESS4_MAP_PATH = makeMapPath("egress4");
+ private static final String CLAT_INGRESS6_MAP_PATH = makeMapPath("ingress6");
+
+ private static String makeMapPath(String which) {
+ return "/sys/fs/bpf/map_clatd_clat_" + which + "_map";
+ }
+
+ private static String makeProgPath(boolean ingress, boolean ether) {
+ String path = "/sys/fs/bpf/prog_clatd_schedcls_"
+ + (ingress ? "ingress6" : "egress4")
+ + "_clat_"
+ + (ether ? "ether" : "rawip");
+ return path;
+ }
+
@NonNull
private final INetd mNetd;
@NonNull
private final Dependencies mDeps;
@Nullable
+ private final IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap;
+ @Nullable
+ private final IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap;
+ @Nullable
private ClatdTracker mClatdTracker = null;
@VisibleForTesting
@@ -195,6 +239,62 @@
public void untagSocket(long cookie) throws IOException {
native_untagSocket(cookie);
}
+
+ /** Get ingress6 BPF map. */
+ @Nullable
+ public IBpfMap<ClatIngress6Key, ClatIngress6Value> getBpfIngress6Map() {
+ // Pre-T devices don't use ClatCoordinator to access clat map. Since Nat464Xlat
+ // initializes a ClatCoordinator object to avoid redundant null pointer check
+ // while using, ignore the BPF map initialization on pre-T devices.
+ // TODO: probably don't initialize ClatCoordinator object on pre-T devices.
+ if (!SdkLevel.isAtLeastT()) return null;
+ try {
+ return new BpfMap<>(CLAT_INGRESS6_MAP_PATH,
+ BpfMap.BPF_F_RDWR, ClatIngress6Key.class, ClatIngress6Value.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create ingress6 map: " + e);
+ return null;
+ }
+ }
+
+ /** Get egress4 BPF map. */
+ @Nullable
+ public IBpfMap<ClatEgress4Key, ClatEgress4Value> getBpfEgress4Map() {
+ // Pre-T devices don't use ClatCoordinator to access clat map. Since Nat464Xlat
+ // initializes a ClatCoordinator object to avoid redundant null pointer check
+ // while using, ignore the BPF map initialization on pre-T devices.
+ // TODO: probably don't initialize ClatCoordinator object on pre-T devices.
+ if (!SdkLevel.isAtLeastT()) return null;
+ try {
+ return new BpfMap<>(CLAT_EGRESS4_MAP_PATH,
+ BpfMap.BPF_F_RDWR, ClatEgress4Key.class, ClatEgress4Value.class);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot create egress4 map: " + e);
+ return null;
+ }
+ }
+
+ /** Checks if the network interface uses an ethernet L2 header. */
+ public boolean isEthernet(String iface) throws IOException {
+ return TcUtils.isEthernet(iface);
+ }
+
+ /** Add a clsact qdisc. */
+ public void tcQdiscAddDevClsact(int ifIndex) throws IOException {
+ TcUtils.tcQdiscAddDevClsact(ifIndex);
+ }
+
+ /** Attach a tc bpf filter. */
+ public void tcFilterAddDevBpf(int ifIndex, boolean ingress, short prio, short proto,
+ String bpfProgPath) throws IOException {
+ TcUtils.tcFilterAddDevBpf(ifIndex, ingress, prio, proto, bpfProgPath);
+ }
+
+ /** Delete a tc filter. */
+ public void tcFilterDelDev(int ifIndex, boolean ingress, short prio, short proto)
+ throws IOException {
+ TcUtils.tcFilterDelDev(ifIndex, ingress, prio, proto);
+ }
}
@VisibleForTesting
@@ -268,6 +368,129 @@
public ClatCoordinator(@NonNull Dependencies deps) {
mDeps = deps;
mNetd = mDeps.getNetd();
+ mIngressMap = mDeps.getBpfIngress6Map();
+ mEgressMap = mDeps.getBpfEgress4Map();
+ }
+
+ private void maybeStartBpf(final ClatdTracker tracker) {
+ if (mIngressMap == null || mEgressMap == null) return;
+
+ final boolean isEthernet;
+ try {
+ isEthernet = mDeps.isEthernet(tracker.iface);
+ } catch (IOException e) {
+ Log.e(TAG, "Fail to call isEthernet for interface " + tracker.iface);
+ return;
+ }
+
+ final ClatEgress4Key txKey = new ClatEgress4Key(tracker.v4ifIndex, tracker.v4);
+ final ClatEgress4Value txValue = new ClatEgress4Value(tracker.ifIndex, tracker.v6,
+ tracker.pfx96, (short) (isEthernet ? 1 /* ETHER */ : 0 /* RAWIP */));
+ try {
+ mEgressMap.insertEntry(txKey, txValue);
+ } catch (ErrnoException | IllegalStateException e) {
+ Log.e(TAG, "Could not insert entry (" + txKey + ", " + txValue + ") on egress map: "
+ + e);
+ return;
+ }
+
+ final ClatIngress6Key rxKey = new ClatIngress6Key(tracker.ifIndex, tracker.pfx96,
+ tracker.v6);
+ final ClatIngress6Value rxValue = new ClatIngress6Value(tracker.v4ifIndex,
+ tracker.v4);
+ try {
+ mIngressMap.insertEntry(rxKey, rxValue);
+ } catch (ErrnoException | IllegalStateException e) {
+ Log.e(TAG, "Could not insert entry (" + rxKey + ", " + rxValue + ") ingress map: "
+ + e);
+ try {
+ mEgressMap.deleteEntry(txKey);
+ } catch (ErrnoException | IllegalStateException e2) {
+ Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e2);
+ }
+ return;
+ }
+
+ // Usually the clsact will be added in netd 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.
+ try {
+ // tc qdisc add dev .. clsact
+ mDeps.tcQdiscAddDevClsact(tracker.v4ifIndex);
+ } catch (IOException e) {
+ Log.e(TAG, "tc qdisc add dev (" + tracker.v4ifIndex + "[" + tracker.v4iface
+ + "]) failure: " + e);
+ try {
+ mEgressMap.deleteEntry(txKey);
+ } catch (ErrnoException | IllegalStateException e2) {
+ Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e2);
+ }
+ try {
+ mIngressMap.deleteEntry(rxKey);
+ } catch (ErrnoException | IllegalStateException e3) {
+ Log.e(TAG, "Could not delete entry (" + rxKey + ") from ingress map: " + e3);
+ }
+ return;
+ }
+
+ // This program will be attached to the v4-* interface which is a TUN and thus always rawip.
+ try {
+ // tc filter add dev .. egress prio 4 protocol ip bpf object-pinned /sys/fs/bpf/...
+ // direct-action
+ mDeps.tcFilterAddDevBpf(tracker.v4ifIndex, EGRESS, (short) PRIO_CLAT, (short) ETH_P_IP,
+ makeProgPath(EGRESS, RAWIP));
+ } catch (IOException e) {
+ Log.e(TAG, "tc filter add dev (" + tracker.v4ifIndex + "[" + tracker.v4iface
+ + "]) egress prio PRIO_CLAT protocol ip failure: " + e);
+
+ // 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.
+
+ try {
+ mEgressMap.deleteEntry(txKey);
+ } catch (ErrnoException | IllegalStateException e2) {
+ Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e2);
+ }
+ try {
+ mIngressMap.deleteEntry(rxKey);
+ } catch (ErrnoException | IllegalStateException e3) {
+ Log.e(TAG, "Could not delete entry (" + rxKey + ") from ingress map: " + e3);
+ }
+ return;
+ }
+
+ try {
+ // tc filter add dev .. ingress prio 4 protocol ipv6 bpf object-pinned /sys/fs/bpf/...
+ // direct-action
+ mDeps.tcFilterAddDevBpf(tracker.ifIndex, INGRESS, (short) PRIO_CLAT,
+ (short) ETH_P_IPV6, makeProgPath(INGRESS, isEthernet));
+ } catch (IOException e) {
+ Log.e(TAG, "tc filter add dev (" + tracker.ifIndex + "[" + tracker.iface
+ + "]) ingress prio PRIO_CLAT protocol ipv6 failure: " + e);
+
+ // The v4- interface clsact is not deleted. See the reason in the error unwinding code
+ // of the egress filter attaching of v4- tun interface.
+
+ try {
+ mDeps.tcFilterDelDev(tracker.v4ifIndex, EGRESS, (short) PRIO_CLAT,
+ (short) ETH_P_IP);
+ } catch (IOException e2) {
+ Log.e(TAG, "tc filter del dev (" + tracker.v4ifIndex + "[" + tracker.v4iface
+ + "]) egress prio PRIO_CLAT protocol ip failure: " + e2);
+ }
+ try {
+ mEgressMap.deleteEntry(txKey);
+ } catch (ErrnoException | IllegalStateException e3) {
+ Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e3);
+ }
+ try {
+ mIngressMap.deleteEntry(rxKey);
+ } catch (ErrnoException | IllegalStateException e4) {
+ Log.e(TAG, "Could not delete entry (" + rxKey + ") from ingress map: " + e4);
+ }
+ return;
+ }
}
/**
@@ -454,9 +677,48 @@
mClatdTracker = new ClatdTracker(iface, ifIndex, tunIface, tunIfIndex, v4, v6, pfx96,
pid, cookie);
+ // [7] Start BPF
+ maybeStartBpf(mClatdTracker);
+
return v6Str;
}
+ private void maybeStopBpf(final ClatdTracker tracker) {
+ 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);
+ try {
+ mEgressMap.deleteEntry(txKey);
+ } catch (ErrnoException | IllegalStateException e) {
+ Log.e(TAG, "Could not delete entry (" + txKey + "): " + e);
+ }
+
+ final ClatIngress6Key rxKey = new ClatIngress6Key(tracker.ifIndex, tracker.pfx96,
+ tracker.v6);
+ try {
+ mIngressMap.deleteEntry(rxKey);
+ } catch (ErrnoException | IllegalStateException e) {
+ Log.e(TAG, "Could not delete entry (" + rxKey + "): " + e);
+ }
+ }
+
/**
* Stop clatd
*/
@@ -466,6 +728,7 @@
}
Log.i(TAG, "Stopping clatd pid=" + mClatdTracker.pid + " on " + mClatdTracker.iface);
+ maybeStopBpf(mClatdTracker);
mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(),
mClatdTracker.v4.getHostAddress(), mClatdTracker.v6.getHostAddress(),
mClatdTracker.pid);
diff --git a/tests/cts/OWNERS b/tests/cts/OWNERS
index d782008..875b4a2 100644
--- a/tests/cts/OWNERS
+++ b/tests/cts/OWNERS
@@ -1,4 +1,3 @@
# Bug template url: http://b/new?component=31808
-# Bug component: 685852 = per-file **IpSec*
set noparent
file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
diff --git a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index 9cd8418..e8add6b 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -146,6 +146,9 @@
@After
fun tearDown() {
+ if (!kernelIsAtLeast(5, 4)) {
+ return;
+ }
agentsToCleanUp.forEach { it.unregister() }
callbacksToCleanUp.forEach { cm.unregisterNetworkCallback(it) }
diff --git a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
index 0c4c370..48cbd03 100644
--- a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
+++ b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
@@ -54,15 +54,18 @@
import android.os.Build;
import android.os.Process;
import android.platform.test.annotations.AppModeFull;
+import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
import com.android.internal.util.HexDump;
import com.android.networkstack.apishim.Ikev2VpnProfileBuilderShimImpl;
import com.android.networkstack.apishim.Ikev2VpnProfileShimImpl;
+import com.android.networkstack.apishim.VpnManagerShimImpl;
import com.android.networkstack.apishim.common.Ikev2VpnProfileBuilderShim;
import com.android.networkstack.apishim.common.Ikev2VpnProfileShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
+import com.android.networkstack.apishim.common.VpnManagerShim;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -93,8 +96,10 @@
@AppModeFull(reason = "Appops state changes disallowed for instant apps (OP_ACTIVATE_PLATFORM_VPN)")
public class Ikev2VpnTest {
private static final String TAG = Ikev2VpnTest.class.getSimpleName();
+
@Rule
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
// Test vectors for IKE negotiation in test mode.
private static final String SUCCESSFUL_IKE_INIT_RESP_V4 =
"46b8eca1e0d72a18b2b5d9006d47a0022120222000000000000002d0220000300000002c01010004030000"
@@ -184,6 +189,8 @@
private static final CtsNetUtils mCtsNetUtils = new CtsNetUtils(sContext);
private static final long TIMEOUT_MS = 15_000;
+ private VpnManagerShim mVmShim = VpnManagerShimImpl.newInstance(sContext);
+
private final X509Certificate mServerRootCa;
private final CertificateAndKey mUserCertKey;
@@ -457,7 +464,7 @@
}
private void checkStartStopVpnProfileBuildsNetworks(@NonNull IkeTunUtils tunUtils,
- boolean testIpv6, boolean requiresValidation)
+ boolean testIpv6, boolean requiresValidation, boolean testSessionKey)
throws Exception {
String serverAddr = testIpv6 ? TEST_SERVER_ADDR_V6 : TEST_SERVER_ADDR_V4;
String initResp = testIpv6 ? SUCCESSFUL_IKE_INIT_RESP_V6 : SUCCESSFUL_IKE_INIT_RESP_V4;
@@ -476,7 +483,13 @@
.clearCapabilities().addTransportType(TRANSPORT_VPN).build();
sCM.registerNetworkCallback(nr, cb);
- sVpnMgr.startProvisionedVpnProfile();
+ if (testSessionKey) {
+ // testSessionKey will never be true if running on <T
+ // startProvisionedVpnProfileSession() should return a non-null & non-empty random UUID.
+ assertFalse(TextUtils.isEmpty(mVmShim.startProvisionedVpnProfileSession()));
+ } else {
+ sVpnMgr.startProvisionedVpnProfile();
+ }
// Inject IKE negotiation
int expectedMsgId = 0;
@@ -519,16 +532,20 @@
private class VerifyStartStopVpnProfileTest implements TestNetworkRunnable.Test {
private final boolean mTestIpv6Only;
private final boolean mRequiresValidation;
+ private final boolean mTestSessionKey;
/**
* Constructs the test
*
* @param testIpv6Only if true, builds a IPv6-only test; otherwise builds a IPv4-only test
* @param requiresValidation whether this VPN should request platform validation
+ * @param testSessionKey if true, start VPN by calling startProvisionedVpnProfileSession()
*/
- VerifyStartStopVpnProfileTest(boolean testIpv6Only, boolean requiresValidation) {
+ VerifyStartStopVpnProfileTest(boolean testIpv6Only, boolean requiresValidation,
+ boolean testSessionKey) {
mTestIpv6Only = testIpv6Only;
mRequiresValidation = requiresValidation;
+ mTestSessionKey = testSessionKey;
}
@Override
@@ -537,7 +554,7 @@
final IkeTunUtils tunUtils = new IkeTunUtils(testIface.getFileDescriptor());
checkStartStopVpnProfileBuildsNetworks(
- tunUtils, mTestIpv6Only, mRequiresValidation);
+ tunUtils, mTestIpv6Only, mRequiresValidation, mTestSessionKey);
}
@Override
@@ -561,10 +578,14 @@
// Requires shell permission to update appops.
runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(false, false)));
+ new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
+ false /* testIpv6Only */, false /* requiresValidation */,
+ false /* testSessionKey */)));
runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(false, true)));
+ new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
+ false /* testIpv6Only */, true /* requiresValidation */,
+ false /* testSessionKey */)));
}
@Test
@@ -573,9 +594,31 @@
// Requires shell permission to update appops.
runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(true, false)));
+ new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
+ true /* testIpv6Only */, false /* requiresValidation */,
+ false /* testSessionKey */)));
runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(true, true)));
+ new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
+ true /* testIpv6Only */, true /* requiresValidation */,
+ false /* testSessionKey */)));
+ }
+
+ @IgnoreUpTo(SC_V2)
+ @Test
+ public void testStartProvisionedVpnProfileSession() throws Exception {
+ assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
+ assumeTrue(TestUtils.shouldTestTApis());
+
+ // Requires shell permission to update appops.
+ runWithShellPermissionIdentity(
+ new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
+ false /* testIpv6Only */, false /* requiresValidation */,
+ true /* testSessionKey */)));
+
+ runWithShellPermissionIdentity(
+ new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
+ true /* testIpv6Only */, false /* requiresValidation */,
+ true /* testSessionKey */)));
}
private static class CertificateAndKey {
diff --git a/tests/cts/netpermission/internetpermission/Android.bp b/tests/cts/netpermission/internetpermission/Android.bp
new file mode 100644
index 0000000..37ad7cb
--- /dev/null
+++ b/tests/cts/netpermission/internetpermission/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsNetTestCasesInternetPermission",
+ defaults: ["cts_defaults"],
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: ["ctstestrunner-axt"],
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+
+}
diff --git a/tests/cts/netpermission/internetpermission/AndroidManifest.xml b/tests/cts/netpermission/internetpermission/AndroidManifest.xml
new file mode 100644
index 0000000..45ef5bd
--- /dev/null
+++ b/tests/cts/netpermission/internetpermission/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.networkpermission.internetpermission.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.networkpermission.internetpermission.cts.InternetPermissionTest"
+ android:label="InternetPermissionTest"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!--
+ The CTS stubs package cannot be used as the target application here,
+ since that requires many permissions to be set. Instead, specify this
+ package itself as the target and include any stub activities needed.
+
+ This test package uses the default InstrumentationTestRunner, because
+ the InstrumentationCtsTestRunner is only available in the stubs
+ package. That runner cannot be added to this package either, since it
+ relies on hidden APIs.
+ -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.networkpermission.internetpermission.cts"
+ android:label="CTS tests for INTERNET permissions">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+
+</manifest>
diff --git a/tests/cts/netpermission/internetpermission/AndroidTest.xml b/tests/cts/netpermission/internetpermission/AndroidTest.xml
new file mode 100644
index 0000000..3b23e72
--- /dev/null
+++ b/tests/cts/netpermission/internetpermission/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS internet permission test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="networking" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="not-shardable" value="true" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsNetTestCasesInternetPermission.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.networkpermission.internetpermission.cts" />
+ <option name="runtime-hint" value="10s" />
+ </test>
+</configuration>
diff --git a/tests/cts/netpermission/internetpermission/TEST_MAPPING b/tests/cts/netpermission/internetpermission/TEST_MAPPING
new file mode 100644
index 0000000..60877f4
--- /dev/null
+++ b/tests/cts/netpermission/internetpermission/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsNetTestCasesInternetPermission"
+ }
+ ]
+}
diff --git a/tests/cts/netpermission/internetpermission/src/android/net/cts/network/permission/InternetPermissionTest.java b/tests/cts/netpermission/internetpermission/src/android/net/cts/network/permission/InternetPermissionTest.java
new file mode 100644
index 0000000..2b7c8b5
--- /dev/null
+++ b/tests/cts/netpermission/internetpermission/src/android/net/cts/network/permission/InternetPermissionTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.net.cts.networkpermission.internetpermission;
+
+import static org.junit.Assert.fail;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Socket;
+/**
+* Test that protected android.net.ConnectivityManager methods cannot be called without
+* permissions
+*/
+@RunWith(AndroidJUnit4.class)
+public class InternetPermissionTest {
+
+ /**
+ * Verify that create inet socket failed because of the permission is missing.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#INTERNET}.
+ */
+ @SmallTest
+ @Test
+ public void testCreateSocket() throws Exception {
+ try {
+ Socket socket = new Socket("example.com", 80);
+ fail("Ceate inet socket did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/cts/netpermission/updatestatspermission/Android.bp b/tests/cts/netpermission/updatestatspermission/Android.bp
new file mode 100644
index 0000000..7a24886
--- /dev/null
+++ b/tests/cts/netpermission/updatestatspermission/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsNetTestCasesUpdateStatsPermission",
+ defaults: ["cts_defaults"],
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: ["ctstestrunner-axt"],
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+
+}
diff --git a/tests/cts/netpermission/updatestatspermission/AndroidManifest.xml b/tests/cts/netpermission/updatestatspermission/AndroidManifest.xml
new file mode 100644
index 0000000..6babe8f
--- /dev/null
+++ b/tests/cts/netpermission/updatestatspermission/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.networkpermission.updatestatspermission.cts">
+
+ <!--
+ This CTS test is designed to test that an unprivileged app cannot get the
+ UPDATE_DEVICE_STATS permission even if it specified it in the manifest. the
+ UPDATE_DEVICE_STATS permission is a signature|privileged permission that CTS
+ test cannot have.
+ -->
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.networkpermission.updatestatspermission.cts.UpdateStatsPermissionTest"
+ android:label="UpdateStatsPermissionTest"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!--
+ The CTS stubs package cannot be used as the target application here,
+ since that requires many permissions to be set. Instead, specify this
+ package itself as the target and include any stub activities needed.
+
+ This test package uses the default InstrumentationTestRunner, because
+ the InstrumentationCtsTestRunner is only available in the stubs
+ package. That runner cannot be added to this package either, since it
+ relies on hidden APIs.
+ -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.networkpermission.updatestatspermission.cts"
+ android:label="CTS tests for UPDATE_DEVICE_STATS permissions">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
+
+</manifest>
diff --git a/tests/cts/netpermission/updatestatspermission/AndroidTest.xml b/tests/cts/netpermission/updatestatspermission/AndroidTest.xml
new file mode 100644
index 0000000..c47cad9
--- /dev/null
+++ b/tests/cts/netpermission/updatestatspermission/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS update stats permission test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="networking" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="not-shardable" value="true" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsNetTestCasesUpdateStatsPermission.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.networkpermission.updatestatspermission.cts" />
+ <option name="runtime-hint" value="10s" />
+ </test>
+</configuration>
diff --git a/tests/cts/netpermission/updatestatspermission/TEST_MAPPING b/tests/cts/netpermission/updatestatspermission/TEST_MAPPING
new file mode 100644
index 0000000..6d6dfe0
--- /dev/null
+++ b/tests/cts/netpermission/updatestatspermission/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsNetTestCasesUpdateStatsPermission"
+ }
+ ]
+}
diff --git a/tests/cts/netpermission/updatestatspermission/src/android/net/cts/network/permission/UpdateStatsPermissionTest.java b/tests/cts/netpermission/updatestatspermission/src/android/net/cts/network/permission/UpdateStatsPermissionTest.java
new file mode 100644
index 0000000..bea843c
--- /dev/null
+++ b/tests/cts/netpermission/updatestatspermission/src/android/net/cts/network/permission/UpdateStatsPermissionTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package android.net.cts.networkpermission.updatestatspermission;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.TrafficStats;
+import android.os.Process;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+* Test that protected android.net.ConnectivityManager methods cannot be called without
+* permissions
+*/
+@RunWith(AndroidJUnit4.class)
+public class UpdateStatsPermissionTest {
+
+ /**
+ * Verify that setCounterSet for a different uid failed because of the permission cannot be
+ * granted to a third-party app.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#UPDATE_DEVICE_STATS}.
+ */
+ @SmallTest
+ @Test
+ public void testUpdateDeviceStatsPermission() throws Exception {
+
+ // Set the current thread uid to a another uid. It should silently fail when tagging the
+ // socket since the current process doesn't have UPDATE_DEVICE_STATS permission.
+ TrafficStats.setThreadStatsTag(0);
+ TrafficStats.setThreadStatsUid(/*root uid*/ 0);
+ Socket socket = new Socket("example.com", 80);
+ TrafficStats.tagSocket(socket);
+
+ // Transfer 1K of data to a remote host and verify the stats is still billed to the current
+ // uid.
+ final int byteCount = 1024;
+
+ socket.setTcpNoDelay(true);
+ socket.setSoLinger(true, 0);
+ OutputStream out = socket.getOutputStream();
+ byte[] buf = new byte[byteCount];
+ final long uidTxBytesBefore = TrafficStats.getUidTxBytes(Process.myUid());
+ out.write(buf);
+ out.close();
+ socket.close();
+ long uidTxBytesAfter = TrafficStats.getUidTxBytes(Process.myUid());
+ long uidTxDeltaBytes = uidTxBytesAfter - uidTxBytesBefore;
+ assertTrue("uidtxb: " + uidTxBytesBefore + " -> " + uidTxBytesAfter + " delta="
+ + uidTxDeltaBytes + " >= " + byteCount, uidTxDeltaBytes >= byteCount);
+ }
+
+ static final int UNSUPPORTED = -1;
+
+ /**
+ * Verify that get TrafficStats of a different uid failed because of the permission is not
+ * granted to a third-party app.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#UPDATE_DEVICE_STATS}.
+ */
+ @SmallTest
+ @Test
+ public void testGetStatsOfOtherUid() throws Exception {
+ // Test get stats of another uid failed since the current process does not have permission
+ assertEquals(UNSUPPORTED, TrafficStats.getUidRxBytes(/*root uid*/ 0));
+ }
+}
diff --git a/tests/native/Android.bp b/tests/native/Android.bp
index cd438f6..9c286d8 100644
--- a/tests/native/Android.bp
+++ b/tests/native/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_test {
name: "connectivity_native_test",
test_suites: [
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index bd925f3..545f7b9 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -130,6 +130,7 @@
"service-connectivity-pre-jarjar",
"service-connectivity-tiramisu-pre-jarjar",
"services.core-vpn",
+ "cts-net-utils"
],
libs: [
"android.net.ipsec.ike.stubs.module_lib",
diff --git a/tests/unit/java/android/net/Ikev2VpnProfileTest.java b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
index 8559c20..8222ca1 100644
--- a/tests/unit/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS;
+
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertArrayEquals;
@@ -25,6 +28,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.os.Build;
import android.test.mock.MockContext;
@@ -441,6 +445,33 @@
assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
}
+ @Test
+ public void testConversionIsLosslessWithIkeTunConnParams() throws Exception {
+ final IkeTunnelConnectionParams tunnelParams =
+ new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+ // Config authentication related fields is not required while building with
+ // IkeTunnelConnectionParams.
+ final Ikev2VpnProfile ikeProfile = new Ikev2VpnProfile.Builder(tunnelParams).build();
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testEquals() throws Exception {
+ // Verify building without IkeTunnelConnectionParams
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ assertEquals(builder.build(), builder.build());
+
+ // Verify building with IkeTunnelConnectionParams
+ final IkeTunnelConnectionParams tunnelParams =
+ new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+ final IkeTunnelConnectionParams tunnelParams2 =
+ new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+ assertEquals(new Ikev2VpnProfile.Builder(tunnelParams).build(),
+ new Ikev2VpnProfile.Builder(tunnelParams2).build());
+ }
+
+
private static class CertificateAndKey {
public final X509Certificate cert;
public final PrivateKey key;
diff --git a/tests/unit/java/com/android/internal/net/VpnProfileTest.java b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
index 943a559..360390d 100644
--- a/tests/unit/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
@@ -16,6 +16,9 @@
package com.android.internal.net;
+import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS;
+
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.testutils.ParcelUtils.assertParcelSane;
@@ -26,6 +29,7 @@
import static org.junit.Assert.assertTrue;
import android.net.IpSecAlgorithm;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.os.Build;
import androidx.test.filters.SmallTest;
@@ -85,7 +89,8 @@
private VpnProfile getSampleIkev2Profile(String key) {
final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
- false /* excludesLocalRoutes */, true /* requiresPlatformValidation */);
+ false /* excludesLocalRoutes */, true /* requiresPlatformValidation */,
+ null /* ikeTunConnParams */);
p.name = "foo";
p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
@@ -120,6 +125,35 @@
return p;
}
+ private VpnProfile getSampleIkev2ProfileWithIkeTunConnParams(String key) {
+ final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
+ false /* excludesLocalRoutes */, true /* requiresPlatformValidation */,
+ new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS));
+
+ p.name = "foo";
+ p.server = "bar";
+ p.dnsServers = "8.8.8.8";
+ p.searchDomains = "";
+ p.routes = "0.0.0.0/0";
+ p.mppe = false;
+ p.proxy = null;
+ p.setAllowedAlgorithms(
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
+ IpSecAlgorithm.AUTH_HMAC_SHA512,
+ IpSecAlgorithm.CRYPT_AES_CBC));
+ p.isBypassable = true;
+ p.isMetered = true;
+ p.maxMtu = 1350;
+ p.areAuthParamsInline = true;
+
+ // Not saved, but also not compared.
+ p.saveLogin = true;
+
+ return p;
+ }
+
@Test
public void testEquals() {
assertEquals(
@@ -134,13 +168,21 @@
public void testParcelUnparcel() {
if (isAtLeastT()) {
// excludeLocalRoutes, requiresPlatformValidation were added in T.
- assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 25);
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 26);
+ assertParcelSane(getSampleIkev2ProfileWithIkeTunConnParams(DUMMY_PROFILE_KEY), 26);
} else {
assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 23);
}
}
@Test
+ public void testEncodeDecodeWithIkeTunConnParams() {
+ final VpnProfile profile = getSampleIkev2ProfileWithIkeTunConnParams(DUMMY_PROFILE_KEY);
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertEquals(profile, decoded);
+ }
+
+ @Test
public void testEncodeDecode() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index 6c8b545..c3d64cb 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -17,11 +17,16 @@
package com.android.server.connectivity;
import static android.net.INetd.IF_STATE_UP;
+import static android.system.OsConstants.ETH_P_IP;
+import static android.system.OsConstants.ETH_P_IPV6;
import static com.android.net.module.util.NetworkStackConstants.ETHER_MTU;
import static com.android.server.connectivity.ClatCoordinator.CLAT_MAX_MTU;
+import static com.android.server.connectivity.ClatCoordinator.EGRESS;
+import static com.android.server.connectivity.ClatCoordinator.INGRESS;
import static com.android.server.connectivity.ClatCoordinator.INIT_V4ADDR_PREFIX_LEN;
import static com.android.server.connectivity.ClatCoordinator.INIT_V4ADDR_STRING;
+import static com.android.server.connectivity.ClatCoordinator.PRIO_CLAT;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertEquals;
@@ -41,6 +46,11 @@
import androidx.test.filters.SmallTest;
+import com.android.net.module.util.IBpfMap;
+import com.android.net.module.util.bpf.ClatEgress4Key;
+import com.android.net.module.util.bpf.ClatEgress4Value;
+import com.android.net.module.util.bpf.ClatIngress6Key;
+import com.android.net.module.util.bpf.ClatIngress6Value;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -98,8 +108,23 @@
private static final ParcelFileDescriptor PACKET_SOCK_PFD = new ParcelFileDescriptor(
new FileDescriptor());
+ private static final String EGRESS_PROG_PATH =
+ "/sys/fs/bpf/prog_clatd_schedcls_egress4_clat_rawip";
+ private static final String INGRESS_PROG_PATH =
+ "/sys/fs/bpf/prog_clatd_schedcls_ingress6_clat_ether";
+ private static final ClatEgress4Key EGRESS_KEY = new ClatEgress4Key(STACKED_IFINDEX,
+ INET4_LOCAL4);
+ private static final ClatEgress4Value EGRESS_VALUE = new ClatEgress4Value(BASE_IFINDEX,
+ INET6_LOCAL6, INET6_PFX96, (short) 1 /* oifIsEthernet, 1 = true */);
+ private static final ClatIngress6Key INGRESS_KEY = new ClatIngress6Key(BASE_IFINDEX,
+ INET6_PFX96, INET6_LOCAL6);
+ private static final ClatIngress6Value INGRESS_VALUE = new ClatIngress6Value(STACKED_IFINDEX,
+ INET4_LOCAL4);
+
@Mock private INetd mNetd;
@Spy private TestDependencies mDeps = new TestDependencies();
+ @Mock private IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap;
+ @Mock private IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap;
/**
* The dependency injection class is used to mock the JNI functions and system functions
@@ -298,6 +323,49 @@
fail("unsupported arg: " + cookie);
}
}
+
+ /** Get ingress6 BPF map. */
+ @Override
+ public IBpfMap<ClatIngress6Key, ClatIngress6Value> getBpfIngress6Map() {
+ return mIngressMap;
+ }
+
+ /** Get egress4 BPF map. */
+ @Override
+ public IBpfMap<ClatEgress4Key, ClatEgress4Value> getBpfEgress4Map() {
+ return mEgressMap;
+ }
+
+ /** Checks if the network interface uses an ethernet L2 header. */
+ public boolean isEthernet(String iface) throws IOException {
+ if (BASE_IFACE.equals(iface)) return true;
+
+ fail("unsupported arg: " + iface);
+ return false;
+ }
+
+ /** Add a clsact qdisc. */
+ @Override
+ public void tcQdiscAddDevClsact(int ifIndex) throws IOException {
+ // no-op
+ return;
+ }
+
+ /** Attach a tc bpf filter. */
+ @Override
+ public void tcFilterAddDevBpf(int ifIndex, boolean ingress, short prio, short proto,
+ String bpfProgPath) throws IOException {
+ // no-op
+ return;
+ }
+
+ /** Delete a tc filter. */
+ @Override
+ public void tcFilterDelDev(int ifIndex, boolean ingress, short prio, short proto)
+ throws IOException {
+ // no-op
+ return;
+ }
};
@NonNull
@@ -322,8 +390,8 @@
@Test
public void testStartStopClatd() throws Exception {
final ClatCoordinator coordinator = makeClatCoordinator();
- final InOrder inOrder = inOrder(mNetd, mDeps);
- clearInvocations(mNetd, mDeps);
+ final InOrder inOrder = inOrder(mNetd, mDeps, mIngressMap, mEgressMap);
+ clearInvocations(mNetd, mDeps, mIngressMap, mEgressMap);
// [1] Start clatd.
final String addr6For464xlat = coordinator.clatStart(BASE_IFACE, NETID, NAT64_IP_PREFIX);
@@ -379,6 +447,13 @@
argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd)),
eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING));
+ inOrder.verify(mEgressMap).insertEntry(eq(EGRESS_KEY), eq(EGRESS_VALUE));
+ inOrder.verify(mIngressMap).insertEntry(eq(INGRESS_KEY), eq(INGRESS_VALUE));
+ inOrder.verify(mDeps).tcQdiscAddDevClsact(eq(STACKED_IFINDEX));
+ inOrder.verify(mDeps).tcFilterAddDevBpf(eq(STACKED_IFINDEX), eq(EGRESS),
+ eq((short) PRIO_CLAT), eq((short) ETH_P_IP), eq(EGRESS_PROG_PATH));
+ inOrder.verify(mDeps).tcFilterAddDevBpf(eq(BASE_IFINDEX), eq(INGRESS),
+ eq((short) PRIO_CLAT), eq((short) ETH_P_IPV6), eq(INGRESS_PROG_PATH));
inOrder.verifyNoMoreInteractions();
// [2] Start clatd again failed.
@@ -388,6 +463,12 @@
// [3] Expect clatd to stop successfully.
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(mIngressMap).deleteEntry(eq(INGRESS_KEY));
inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE));
diff --git a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
index 4d3e4d3..dfb4fcc 100644
--- a/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
+++ b/tests/unit/java/com/android/server/ethernet/EthernetNetworkFactoryTest.java
@@ -20,9 +20,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
@@ -41,8 +41,8 @@
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
-import android.net.EthernetNetworkSpecifier;
import android.net.EthernetNetworkManagementException;
+import android.net.EthernetNetworkSpecifier;
import android.net.INetworkInterfaceOutcomeReceiver;
import android.net.IpConfiguration;
import android.net.LinkAddress;
@@ -59,14 +59,11 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.test.TestLooper;
-import android.util.Pair;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.connectivity.resources.R;
import com.android.net.module.util.InterfaceParams;
-
import com.android.testutils.DevSdkIgnoreRule;
import org.junit.After;
@@ -79,6 +76,7 @@
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -362,7 +360,7 @@
assertFalse(ret);
verifyNoStopOrStart();
- listener.expectOnErrorWithMessage("can't be updated as it is not available");
+ listener.expectOnError();
}
@Test
@@ -376,7 +374,7 @@
assertFalse(ret);
verifyNoStopOrStart();
- listener.expectOnErrorWithMessage("No changes");
+ listener.expectOnError();
}
@Test
@@ -626,8 +624,6 @@
private static final class TestNetworkManagementListener
implements INetworkInterfaceOutcomeReceiver {
private final CompletableFuture<String> mResult = new CompletableFuture<>();
- private final CompletableFuture<EthernetNetworkManagementException> mError =
- new CompletableFuture<>();
@Override
public void onResult(@NonNull String iface) {
@@ -636,19 +632,21 @@
@Override
public void onError(@NonNull EthernetNetworkManagementException exception) {
- mError.complete(exception);
+ mResult.completeExceptionally(exception);
}
String expectOnResult() throws Exception {
return mResult.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
}
- EthernetNetworkManagementException expectOnError() throws Exception {
- return mError.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
- }
-
- void expectOnErrorWithMessage(String msg) throws Exception {
- assertTrue(expectOnError().getMessage().contains(msg));
+ void expectOnError() throws Exception {
+ assertThrows(EthernetNetworkManagementException.class, () -> {
+ try {
+ mResult.get();
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ }
+ });
}
@Override
@@ -723,7 +721,7 @@
mNetFactory.updateInterface(iface, ipConfiguration, capabilities, failedListener);
interruptingRunnable.run();
- failedListener.expectOnErrorWithMessage("aborted");
+ failedListener.expectOnError();
}
@Test
@@ -754,7 +752,7 @@
mNetFactory.updateInterface(TEST_IFACE, ipConfiguration, capabilities, listener);
verifyNoStopOrStart();
- listener.expectOnErrorWithMessage("can't be updated as it is not available");
+ listener.expectOnError();
}
@Test