diff options
18 files changed, 476 insertions, 81 deletions
diff --git a/common/Android.bp b/common/Android.bp index ef801700..bdd0c742 100644 --- a/common/Android.bp +++ b/common/Android.bp @@ -110,7 +110,9 @@ filegroup { java_library { name: "net-utils-device-common-bpf", srcs: [ + "device/com/android/net/module/util/BpfBitmap.java", "device/com/android/net/module/util/BpfMap.java", + "device/com/android/net/module/util/BpfUtils.java", "device/com/android/net/module/util/HexDump.java", "device/com/android/net/module/util/IBpfMap.java", "device/com/android/net/module/util/JniUtil.java", @@ -234,7 +236,7 @@ java_library { libs: [ "framework-annotations-lib", "framework-connectivity.stubs.module_lib", - "framework-connectivity-tiramisu.stubs.module_lib", + "framework-connectivity-t.stubs.module_lib", ], jarjar_rules: "jarjar-rules-shared.txt", visibility: [ diff --git a/common/device/com/android/net/module/util/BpfBitmap.java b/common/device/com/android/net/module/util/BpfBitmap.java new file mode 100644 index 00000000..0c8bb37e --- /dev/null +++ b/common/device/com/android/net/module/util/BpfBitmap.java @@ -0,0 +1,126 @@ +/* + * 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. + */ + +package com.android.net.module.util; + +import android.system.ErrnoException; + +import androidx.annotation.NonNull; + + /** + * + * Generic bitmap class for use with BPF programs. Corresponds to a BpfMap + * array type with key->int and value->uint64_t defined in the bpf program. + * + */ +public class BpfBitmap { + private BpfMap<Struct.U32, Struct.S64> mBpfMap; + + /** + * Create a BpfBitmap map wrapper with "path" of filesystem. + * + * @param path The path of the BPF map. + */ + public BpfBitmap(@NonNull String path) throws ErrnoException { + mBpfMap = new BpfMap<Struct.U32, Struct.S64>(path, BpfMap.BPF_F_RDWR, + Struct.U32.class, Struct.S64.class); + } + + /** + * Retrieves the value from BpfMap for the given key. + * + * @param key The key in the map corresponding to the value to return. + */ + private long getBpfMapValue(Struct.U32 key) throws ErrnoException { + Struct.S64 curVal = mBpfMap.getValue(key); + if (curVal != null) { + return curVal.val; + } else { + return 0; + } + } + + /** + * Retrieves the bit for the given index in the bitmap. + * + * @param index Position in bitmap. + */ + public boolean get(int index) throws ErrnoException { + if (index < 0) return false; + + Struct.U32 key = new Struct.U32(index >> 6); + return ((getBpfMapValue(key) >>> (index & 63)) & 1L) != 0; + } + + /** + * Set the specified index in the bitmap. + * + * @param index Position to set in bitmap. + */ + public void set(int index) throws ErrnoException { + set(index, true); + } + + /** + * Unset the specified index in the bitmap. + * + * @param index Position to unset in bitmap. + */ + public void unset(int index) throws ErrnoException { + set(index, false); + } + + /** + * Change the specified index in the bitmap to set value. + * + * @param index Position to unset in bitmap. + * @param set Boolean indicating to set or unset index. + */ + public void set(int index, boolean set) throws ErrnoException { + if (index < 0) throw new IllegalArgumentException("Index out of bounds."); + + Struct.U32 key = new Struct.U32(index >> 6); + long mask = (1L << (index & 63)); + long val = getBpfMapValue(key); + if (set) val |= mask; else val &= ~mask; + mBpfMap.updateEntry(key, new Struct.S64(val)); + } + + /** + * Clears the map. The map may already be empty. + * + * @throws ErrnoException if updating entry to 0 fails. + */ + public void clear() throws ErrnoException { + mBpfMap.forEach((key, value) -> { + mBpfMap.updateEntry(key, new Struct.S64(0)); + }); + } + + /** + * Checks if all bitmap values are 0. + */ + public boolean isEmpty() throws ErrnoException { + Struct.U32 key = mBpfMap.getFirstKey(); + while (key != null) { + if (getBpfMapValue(key) != 0) { + return false; + } + key = mBpfMap.getNextKey(key); + } + return true; + } +} diff --git a/common/device/com/android/net/module/util/BpfMap.java b/common/device/com/android/net/module/util/BpfMap.java index b42c3885..d4ce83b4 100644 --- a/common/device/com/android/net/module/util/BpfMap.java +++ b/common/device/com/android/net/module/util/BpfMap.java @@ -30,7 +30,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.NoSuchElementException; import java.util.Objects; -import java.util.function.BiConsumer; /** * BpfMap is a key -> value mapping structure that is designed to maintained the bpf map entries. @@ -250,7 +249,7 @@ public class BpfMap<K extends Struct, V extends Struct> implements IBpfMap<K, V> * Otherwise, iteration will result in undefined behaviour. */ @Override - public void forEach(BiConsumer<K, V> action) throws ErrnoException { + public void forEach(ThrowingBiConsumer<K, V> action) throws ErrnoException { @Nullable K nextKey = getFirstKey(); while (nextKey != null) { diff --git a/common/device/com/android/net/module/util/BpfUtils.java b/common/device/com/android/net/module/util/BpfUtils.java new file mode 100644 index 00000000..94af11b3 --- /dev/null +++ b/common/device/com/android/net/module/util/BpfUtils.java @@ -0,0 +1,69 @@ +/* + * 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. + */ +package com.android.net.module.util; + +import androidx.annotation.NonNull; + +import java.io.IOException; + +/** + * The classes and the methods for BPF utilization. + * + * {@hide} + */ +public class BpfUtils { + static { + System.loadLibrary(JniUtil.getJniLibraryName(BpfUtils.class.getPackage())); + } + + // Defined in include/uapi/linux/bpf.h. Only adding the CGROUPS currently being used for now. + public static final int BPF_CGROUP_INET_INGRESS = 0; + public static final int BPF_CGROUP_INET_EGRESS = 1; + public static final int BPF_CGROUP_INET4_BIND = 8; + public static final int BPF_CGROUP_INET6_BIND = 9; + + + /** + * Attach BPF program to CGROUP + */ + public static void attachProgram(int type, @NonNull String programPath, + @NonNull String cgroupPath, int flags) throws IOException { + native_attachProgramToCgroup(type, programPath, cgroupPath, flags); + } + + /** + * Detach BPF program from CGROUP + */ + public static void detachProgram(int type, @NonNull String cgroupPath) + throws IOException { + native_detachProgramFromCgroup(type, cgroupPath); + } + + /** + * Detach single BPF program from CGROUP + */ + public static void detachSingleProgram(int type, @NonNull String programPath, + @NonNull String cgroupPath) throws IOException { + native_detachSingleProgramFromCgroup(type, programPath, cgroupPath); + } + + private static native boolean native_attachProgramToCgroup(int type, String programPath, + String cgroupPath, int flags) throws IOException; + private static native boolean native_detachProgramFromCgroup(int type, String cgroupPath) + throws IOException; + private static native boolean native_detachSingleProgramFromCgroup(int type, + String programPath, String cgroupPath) throws IOException; +} diff --git a/common/device/com/android/net/module/util/IBpfMap.java b/common/device/com/android/net/module/util/IBpfMap.java index 708cf61a..d43b22c6 100644 --- a/common/device/com/android/net/module/util/IBpfMap.java +++ b/common/device/com/android/net/module/util/IBpfMap.java @@ -64,10 +64,14 @@ public interface IBpfMap<K extends Struct, V extends Struct> { /** Retrieve a value from the map. */ V getValue(@NonNull K key) throws ErrnoException; + public interface ThrowingBiConsumer<T,U> { + void accept(T t, U u) throws ErrnoException; + } + /** * Iterate through the map and handle each key -> value retrieved base on the given BiConsumer. */ - void forEach(BiConsumer<K, V> action) throws ErrnoException; + void forEach(ThrowingBiConsumer<K, V> action) throws ErrnoException; /** Clears the map. */ void clear() throws ErrnoException; diff --git a/common/framework/com/android/net/module/util/CollectionUtils.java b/common/framework/com/android/net/module/util/CollectionUtils.java index 312ca483..a16ef337 100644 --- a/common/framework/com/android/net/module/util/CollectionUtils.java +++ b/common/framework/com/android/net/module/util/CollectionUtils.java @@ -193,4 +193,30 @@ public final class CollectionUtils { } return total; } + + /** + * Returns true if the first collection contains any of the elements of the second. + * @param haystack where to search + * @param needles what to search for + * @param <T> type of elements + * @return true if |haystack| contains any of the |needles|, false otherwise + */ + public static <T> boolean containsAny(Collection<T> haystack, Collection<? extends T> needles) { + for (T needle : needles) { + if (haystack.contains(needle)) return true; + } + return false; + } + + /** + * Returns true if the first collection contains all of the elements of the second. + * @param haystack where to search + * @param needles what to search for + * @param <T> type of elements + * @return true if |haystack| contains all of the |needles|, false otherwise + */ + public static <T> boolean containsAll(Collection<T> haystack, Collection<? extends T> needles) { + return haystack.containsAll(needles); + } + } diff --git a/common/framework/com/android/net/module/util/PermissionUtils.java b/common/framework/com/android/net/module/util/PermissionUtils.java index 0f3dc156..be5b0cde 100644 --- a/common/framework/com/android/net/module/util/PermissionUtils.java +++ b/common/framework/com/android/net/module/util/PermissionUtils.java @@ -19,17 +19,21 @@ package com.android.net.module.util; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.NETWORK_STACK; +import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.PackageInfo; import android.os.Binder; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Collection of permission utilities. @@ -144,4 +148,25 @@ public final class PermissionUtils { throw new UnsupportedOperationException(errorMessage); } } + + /** + * Get the list of granted permissions for a package info. + * + * PackageInfo contains the list of requested permissions, and their state (whether they + * were granted or not, in particular) as a parallel array. Most users care only about + * granted permissions. This method returns the list of them. + * + * @param packageInfo the package info for the relevant uid. + * @return the list of granted permissions. + */ + public static List<String> getGrantedPermissions(final @NonNull PackageInfo packageInfo) { + if (null == packageInfo.requestedPermissions) return Collections.emptyList(); + final ArrayList<String> result = new ArrayList<>(packageInfo.requestedPermissions.length); + for (int i = 0; i < packageInfo.requestedPermissions.length; ++i) { + if (0 != (REQUESTED_PERMISSION_GRANTED & packageInfo.requestedPermissionsFlags[i])) { + result.add(packageInfo.requestedPermissions[i]); + } + } + return result; + } } diff --git a/common/native/bpf_headers/Android.bp b/common/native/bpf_headers/Android.bp index 834ef02b..4c8e728f 100644 --- a/common/native/bpf_headers/Android.bp +++ b/common/native/bpf_headers/Android.bp @@ -35,29 +35,6 @@ cc_library_headers { "com.android.tethering", "com.android.art.debug", ], - visibility: [ - "//bootable/libbootloader/vts", - "//cts/tests/tests/net/native", - "//frameworks/base/services/core/jni", - "//frameworks/native/libs/cputimeinstate", - "//frameworks/native/services/gpuservice", - "//frameworks/native/services/gpuservice/gpumem", - "//frameworks/native/services/gpuservice/tests/unittests", - "//frameworks/native/services/gpuservice/tracing", - "//packages/modules/Connectivity/bpf_progs", - "//packages/modules/Connectivity/netd", - "//packages/modules/Connectivity/service/native", - "//packages/modules/Connectivity/service/native/libs/libclat", - "//packages/modules/Connectivity/tests/unit/jni", - "//packages/modules/DnsResolver/tests", - "//system/bpf/bpfloader", - "//system/bpf/libbpf_android", - "//system/memory/libmeminfo", - "//system/netd/server", - "//system/netd/tests", - "//system/netd/tests/benchmarks", - "//test/vts-testcase/kernel/api/bpf_native_test", - ], } @@ -73,9 +50,9 @@ cc_test { "-Werror", "-Wno-error=unused-variable", ], + header_libs: ["bpf_headers"], static_libs: ["libgmock"], shared_libs: [ - "libbpf_android", "libbase", "liblog", "libutils", diff --git a/common/native/bpf_headers/include/bpf/BpfUtils.h b/common/native/bpf_headers/include/bpf/BpfUtils.h index 265d4b63..8f1b9a28 100644 --- a/common/native/bpf_headers/include/bpf/BpfUtils.h +++ b/common/native/bpf_headers/include/bpf/BpfUtils.h @@ -112,38 +112,28 @@ static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsign return kernelVersion() >= KVER(major, minor, sub); } -#define SKIP_IF_BPF_SUPPORTED \ - do { \ - if (android::bpf::isAtLeastKernelVersion(4, 9, 0)) { \ - GTEST_LOG_(INFO) << "This test is skipped since bpf is supported\n"; \ - return; \ - } \ +#define SKIP_IF_BPF_SUPPORTED \ + do { \ + if (android::bpf::isAtLeastKernelVersion(4, 9, 0)) \ + GTEST_SKIP() << "Skip: bpf is supported."; \ } while (0) -#define SKIP_IF_BPF_NOT_SUPPORTED \ - do { \ - if (!android::bpf::isAtLeastKernelVersion(4, 9, 0)) { \ - GTEST_LOG_(INFO) << "This test is skipped since bpf is not supported\n"; \ - return; \ - } \ +#define SKIP_IF_BPF_NOT_SUPPORTED \ + do { \ + if (!android::bpf::isAtLeastKernelVersion(4, 9, 0)) \ + GTEST_SKIP() << "Skip: bpf is not supported."; \ } while (0) -#define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED \ - do { \ - if (!android::bpf::isAtLeastKernelVersion(4, 14, 0)) { \ - GTEST_LOG_(INFO) << "This test is skipped since extended bpf feature" \ - << "not supported\n"; \ - return; \ - } \ +#define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED \ + do { \ + if (!android::bpf::isAtLeastKernelVersion(4, 14, 0)) \ + GTEST_SKIP() << "Skip: extended bpf feature not supported."; \ } while (0) -#define SKIP_IF_XDP_NOT_SUPPORTED \ - do { \ - if (!android::bpf::isAtLeastKernelVersion(5, 9, 0)) { \ - GTEST_LOG_(INFO) << "This test is skipped since xdp" \ - << "not supported\n"; \ - return; \ - } \ +#define SKIP_IF_XDP_NOT_SUPPORTED \ + do { \ + if (!android::bpf::isAtLeastKernelVersion(5, 9, 0)) \ + GTEST_SKIP() << "Skip: xdp not supported."; \ } while (0) } // namespace bpf diff --git a/common/native/bpf_syscall_wrappers/Android.bp b/common/native/bpf_syscall_wrappers/Android.bp index a20eed3a..f41a2446 100644 --- a/common/native/bpf_syscall_wrappers/Android.bp +++ b/common/native/bpf_syscall_wrappers/Android.bp @@ -30,20 +30,8 @@ cc_library_headers { min_sdk_version: "30", apex_available: [ "//apex_available:platform", + "com.android.art.debug", "com.android.mediaprovider", "com.android.tethering", ], - visibility: [ - "//frameworks/libs/net/common/native/bpf_headers", - "//frameworks/libs/net/common/native/bpfmapjni", - "//frameworks/libs/net/common/native/tcutils", - "//packages/modules/Connectivity/netd", - "//packages/modules/Connectivity/service", - "//packages/modules/Connectivity/service/native", - "//packages/modules/Connectivity/service/native/libs/libclat", - "//packages/modules/Connectivity/Tethering", - "//packages/providers/MediaProvider/jni", - "//system/bpf/libbpf_android", - "//system/memory/lmkd", - ], } diff --git a/common/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h b/common/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h index 72eebf3c..abf83daa 100644 --- a/common/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h +++ b/common/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h @@ -125,11 +125,12 @@ inline int retrieveProgram(const char* pathname) { } inline int attachProgram(bpf_attach_type type, const BPF_FD_TYPE prog_fd, - const BPF_FD_TYPE cg_fd) { + const BPF_FD_TYPE cg_fd, uint32_t flags = 0) { return bpf(BPF_PROG_ATTACH, { .target_fd = BPF_FD_TO_U32(cg_fd), .attach_bpf_fd = BPF_FD_TO_U32(prog_fd), .attach_type = type, + .attach_flags = flags, }); } @@ -140,6 +141,15 @@ inline int detachProgram(bpf_attach_type type, const BPF_FD_TYPE cg_fd) { }); } +inline int detachSingleProgram(bpf_attach_type type, const BPF_FD_TYPE prog_fd, + const BPF_FD_TYPE cg_fd) { + return bpf(BPF_PROG_DETACH, { + .target_fd = BPF_FD_TO_U32(cg_fd), + .attach_bpf_fd = BPF_FD_TO_U32(prog_fd), + .attach_type = type, + }); +} + } // namespace bpf } // namespace android diff --git a/common/native/bpfutiljni/Android.bp b/common/native/bpfutiljni/Android.bp new file mode 100644 index 00000000..39a27950 --- /dev/null +++ b/common/native/bpfutiljni/Android.bp @@ -0,0 +1,44 @@ +// 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. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_library_static { + name: "libnet_utils_device_common_bpfutils", + srcs: ["com_android_net_module_util_BpfUtils.cpp"], + header_libs: [ + "bpf_syscall_wrappers", + "jni_headers", + ], + shared_libs: [ + "libbase", + "liblog", + "libnativehelper_compat_libc++", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + ], + min_sdk_version: "30", + apex_available: [ + "com.android.tethering", + "//apex_available:platform", + ], + visibility: [ + "//packages/modules/Connectivity/service", + ], +} diff --git a/common/native/bpfutiljni/com_android_net_module_util_BpfUtils.cpp b/common/native/bpfutiljni/com_android_net_module_util_BpfUtils.cpp new file mode 100644 index 00000000..0f2ebbd4 --- /dev/null +++ b/common/native/bpfutiljni/com_android_net_module_util_BpfUtils.cpp @@ -0,0 +1,132 @@ +/* + * 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 <android-base/unique_fd.h> +#include <errno.h> +#include <fcntl.h> +#include <jni.h> +#include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/scoped_utf_chars.h> +#include <string.h> +#include <sys/socket.h> + +#include "BpfSyscallWrappers.h" + +namespace android { + +using base::unique_fd; + +// If attach fails throw error and return false. +static jboolean com_android_net_module_util_BpfUtil_attachProgramToCgroup(JNIEnv *env, + jobject clazz, jint type, jstring bpfProgPath, jstring cgroupPath, jint flags) { + + ScopedUtfChars dirPath(env, cgroupPath); + unique_fd cg_fd(open(dirPath.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)); + if (cg_fd == -1) { + jniThrowExceptionFmt(env, "java/io/IOException", + "Failed to open the cgroup directory %s: %s", + dirPath.c_str(), strerror(errno)); + return false; + } + + ScopedUtfChars bpfProg(env, bpfProgPath); + unique_fd bpf_fd(bpf::retrieveProgram(bpfProg.c_str())); + if (bpf_fd == -1) { + jniThrowExceptionFmt(env, "java/io/IOException", + "Failed to retrieve bpf program from %s: %s", + bpfProg.c_str(), strerror(errno)); + return false; + } + if (bpf::attachProgram((bpf_attach_type) type, bpf_fd, cg_fd, flags)) { + jniThrowExceptionFmt(env, "java/io/IOException", + "Failed to attach bpf program %s to %s: %s", + bpfProg.c_str(), dirPath.c_str(), strerror(errno)); + return false; + } + return true; +} + +// If detach fails throw error and return false. +static jboolean com_android_net_module_util_BpfUtil_detachProgramFromCgroup(JNIEnv *env, + jobject clazz, jint type, jstring cgroupPath) { + + ScopedUtfChars dirPath(env, cgroupPath); + unique_fd cg_fd(open(dirPath.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)); + if (cg_fd == -1) { + jniThrowExceptionFmt(env, "java/io/IOException", + "Failed to open the cgroup directory %s: %s", + dirPath.c_str(), strerror(errno)); + return false; + } + + if (bpf::detachProgram((bpf_attach_type) type, cg_fd)) { + jniThrowExceptionFmt(env, "Failed to detach bpf program from %s: %s", + dirPath.c_str(), strerror(errno)); + return false; + } + return true; +} + +// If detach single program fails throw error and return false. +static jboolean com_android_net_module_util_BpfUtil_detachSingleProgramFromCgroup(JNIEnv *env, + jobject clazz, jint type, jstring bpfProgPath, jstring cgroupPath) { + + ScopedUtfChars dirPath(env, cgroupPath); + unique_fd cg_fd(open(dirPath.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)); + if (cg_fd == -1) { + jniThrowExceptionFmt(env, "java/io/IOException", + "Failed to open the cgroup directory %s: %s", + dirPath.c_str(), strerror(errno)); + return false; + } + + ScopedUtfChars bpfProg(env, bpfProgPath); + unique_fd bpf_fd(bpf::retrieveProgram(bpfProg.c_str())); + if (bpf_fd == -1) { + jniThrowExceptionFmt(env, "java/io/IOException", + "Failed to retrieve bpf program from %s: %s", + bpfProg.c_str(), strerror(errno)); + return false; + } + if (bpf::detachSingleProgram((bpf_attach_type) type, bpf_fd, cg_fd)) { + jniThrowExceptionFmt(env, "Failed to detach bpf program %s from %s: %s", + bpfProg.c_str(), dirPath.c_str(), strerror(errno)); + return false; + } + return true; +} + +/* + * JNI registration. + */ +static const JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "native_attachProgramToCgroup", "(ILjava/lang/String;Ljava/lang/String;I)Z", + (void*) com_android_net_module_util_BpfUtil_attachProgramToCgroup }, + { "native_detachProgramFromCgroup", "(ILjava/lang/String;)Z", + (void*) com_android_net_module_util_BpfUtil_detachProgramFromCgroup }, + { "native_detachSingleProgramFromCgroup", "(ILjava/lang/String;Ljava/lang/String;)Z", + (void*) com_android_net_module_util_BpfUtil_detachSingleProgramFromCgroup }, +}; + +int register_com_android_net_module_util_BpfUtils(JNIEnv* env, char const* class_name) { + return jniRegisterNativeMethods(env, + class_name, + gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/common/native/tcutils/tcutils.cpp b/common/native/tcutils/tcutils.cpp index 0e17f67c..144a4c94 100644 --- a/common/native/tcutils/tcutils.cpp +++ b/common/native/tcutils/tcutils.cpp @@ -196,7 +196,7 @@ public: .acts = { .attr = { .nla_len = sizeof(mRequest.opt.acts), - .nla_type = TCA_U32_ACT, + .nla_type = TCA_MATCHALL_ACT, }, .act1 = { .attr = { diff --git a/common/testutils/Android.bp b/common/testutils/Android.bp index 1a1328f5..642544ad 100644 --- a/common/testutils/Android.bp +++ b/common/testutils/Android.bp @@ -32,7 +32,6 @@ java_library { ], static_libs: [ "androidx.test.ext.junit", - "compatibility-device-util-axt", "kotlin-reflect", "libnanohttpd", "net-tests-utils-host-device-common", diff --git a/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java b/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java index f2ad1e27..d1037488 100644 --- a/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java +++ b/common/testutils/devicetests/com/android/testutils/DumpTestUtils.java @@ -16,7 +16,7 @@ package com.android.testutils; -import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; +import static com.android.testutils.TestPermissionUtil.runAsShell; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -70,8 +70,7 @@ public class DumpTestUtils { final String what = "service '" + serviceName + "' with args: " + Arrays.toString(args); try { if (adoptPermission) { - runWithShellPermissionIdentity(() -> ib.dump(pipe[1], args), - android.Manifest.permission.DUMP); + runAsShell(android.Manifest.permission.DUMP, () -> ib.dump(pipe[1], args)); } else { ib.dump(pipe[1], args); } diff --git a/common/testutils/devicetests/com/android/testutils/TestBpfMap.java b/common/testutils/devicetests/com/android/testutils/TestBpfMap.java index 5614a99a..38835113 100644 --- a/common/testutils/devicetests/com/android/testutils/TestBpfMap.java +++ b/common/testutils/devicetests/com/android/testutils/TestBpfMap.java @@ -21,14 +21,14 @@ import android.system.ErrnoException; import androidx.annotation.NonNull; import com.android.net.module.util.BpfMap; +import com.android.net.module.util.IBpfMap.ThrowingBiConsumer; import com.android.net.module.util.Struct; -import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; -import java.util.function.BiConsumer; +import java.util.concurrent.ConcurrentHashMap; /** * @@ -42,14 +42,14 @@ import java.util.function.BiConsumer; * @param <V> the value type */ public class TestBpfMap<K extends Struct, V extends Struct> extends BpfMap<K, V> { - private final HashMap<K, V> mMap = new HashMap<K, V>(); + private final ConcurrentHashMap<K, V> mMap = new ConcurrentHashMap<>(); public TestBpfMap(final Class<K> key, final Class<V> value) { super(key, value); } @Override - public void forEach(BiConsumer<K, V> action) throws ErrnoException { + public void forEach(ThrowingBiConsumer<K, V> action) throws ErrnoException { // TODO: consider using mocked #getFirstKey and #getNextKey to iterate. It helps to // implement the entry deletion in the iteration if required. for (Map.Entry<K, V> entry : mMap.entrySet()) { diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt index c2b5a5c8..dffdbe8b 100644 --- a/common/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt +++ b/common/testutils/devicetests/com/android/testutils/TestableNetworkCallback.kt @@ -41,6 +41,7 @@ import kotlin.test.fail object NULL_NETWORK : Network(-1) object ANY_NETWORK : Network(-2) +fun anyNetwork() = ANY_NETWORK private val Int.capabilityName get() = NetworkCapabilities.capabilityNameOf(this) @@ -287,7 +288,7 @@ open class TestableNetworkCallback private constructor( fun expectAvailableCallbacks( net: Network, suspended: Boolean = false, - validated: Boolean = true, + validated: Boolean? = true, blocked: Boolean = false, tmt: Long = defaultTimeoutMs ) { @@ -309,14 +310,18 @@ open class TestableNetworkCallback private constructor( private fun expectAvailableCallbacksCommon( net: Network, suspended: Boolean, - validated: Boolean, + validated: Boolean?, tmt: Long ) { expectCallback<Available>(net, tmt) if (suspended) { expectCallback<Suspended>(net, tmt) } - expectCapabilitiesThat(net, tmt) { validated == it.hasCapability(NET_CAPABILITY_VALIDATED) } + expectCapabilitiesThat(net, tmt) { + validated == null || validated == it.hasCapability( + NET_CAPABILITY_VALIDATED + ) + } expectCallback<LinkPropertiesChanged>(net, tmt) } @@ -385,7 +390,7 @@ open class TestableNetworkCallback private constructor( val network = n ?: NULL_NETWORK // TODO : remove this .java access if the tests ever use kotlin-reflect. At the time of // this writing this would be the only use of this library in the tests. - assertTrue(type.java.isInstance(it) && it.network == network, + assertTrue(type.java.isInstance(it) && (ANY_NETWORK === n || it.network == network), "Unexpected callback : $it, expected ${type.java} with Network[$network]") } as T |