Snap for 11975806 from b9e24e1494a327594900fc10681f232dbd4b84bd to 24Q3-release
Change-Id: Ib4cd69e25d1c59dc276b6efc1b98aa404a7e1985
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 8ed5ac0..8a5d249 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -101,6 +101,7 @@
"block.o",
"clatd.o",
"dscpPolicy.o",
+ "gentle.o",
"netd.o",
"offload.o",
"offload@mainline.o",
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index 1958aa8..9e67415 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -85,6 +85,16 @@
}
bpf {
+ name: "gentle.o",
+ srcs: ["gentle.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ sub_dir: "net_shared",
+}
+
+bpf {
name: "offload.o",
srcs: ["offload.c"],
cflags: [
diff --git a/bpf_progs/block.c b/bpf_progs/block.c
index e467203..353525e 100644
--- a/bpf_progs/block.c
+++ b/bpf_progs/block.c
@@ -76,3 +76,4 @@
LICENSE("Apache 2.0");
CRITICAL("ConnectivityNative");
DISABLE_BTF_ON_USER_BUILDS();
+DISABLE_ON_MAINLINE_BEFORE_U_QPR3();
diff --git a/bpf_progs/clatd.c b/bpf_progs/clatd.c
index 9e0d4c4..95e9a4c 100644
--- a/bpf_progs/clatd.c
+++ b/bpf_progs/clatd.c
@@ -431,3 +431,4 @@
LICENSE("Apache 2.0");
CRITICAL("Connectivity");
DISABLE_BTF_ON_USER_BUILDS();
+DISABLE_ON_MAINLINE_BEFORE_U_QPR3();
diff --git a/bpf_progs/dscpPolicy.c b/bpf_progs/dscpPolicy.c
index 1739c37..8bb8ad5 100644
--- a/bpf_progs/dscpPolicy.c
+++ b/bpf_progs/dscpPolicy.c
@@ -239,3 +239,4 @@
LICENSE("Apache 2.0");
CRITICAL("Connectivity");
DISABLE_BTF_ON_USER_BUILDS();
+DISABLE_ON_MAINLINE_BEFORE_U_QPR3();
diff --git a/bpf_progs/gentle.c b/bpf_progs/gentle.c
new file mode 100644
index 0000000..ab9620e
--- /dev/null
+++ b/bpf_progs/gentle.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 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 BPFLOADER_MIN_VER BPFLOADER_MAINLINE_T_VERSION
+
+#include "bpf_helpers.h"
+#include "bpf_net_helpers.h"
+
+DEFINE_BPF_MAP_GRW(test, ARRAY, int, uint64_t, 1, AID_SYSTEM)
+
+DEFINE_BPF_PROG("skfilter/accept", AID_ROOT, AID_SYSTEM, accept)
+(struct __sk_buff *skb) {
+ return 1;
+}
+
+LICENSE("Apache 2.0");
+DISABLE_BTF_ON_USER_BUILDS();
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index 15ed206..bd7b8b4 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -97,6 +97,9 @@
DEFINE_BPF_MAP_NO_NETD(ingress_discard_map, HASH, IngressDiscardKey, IngressDiscardValue,
INGRESS_DISCARD_MAP_SIZE)
+DEFINE_BPF_MAP_RW_NETD(lock_array_test_map, ARRAY, uint32_t, bool, 1)
+DEFINE_BPF_MAP_RW_NETD(lock_hash_test_map, HASH, uint32_t, bool, 1)
+
/* never actually used from ebpf */
DEFINE_BPF_MAP_NO_NETD(iface_index_name_map, HASH, uint32_t, IfaceValue, IFACE_INDEX_NAME_MAP_SIZE)
@@ -676,3 +679,4 @@
LICENSE("Apache 2.0");
CRITICAL("Connectivity and netd");
DISABLE_BTF_ON_USER_BUILDS();
+DISABLE_ON_MAINLINE_BEFORE_U_QPR3();
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 8c3011b..c7ac059 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -878,3 +878,4 @@
LICENSE("Apache 2.0");
CRITICAL("Connectivity (Tethering)");
DISABLE_BTF_ON_USER_BUILDS();
+DISABLE_ON_MAINLINE_BEFORE_U_QPR3();
diff --git a/bpf_progs/test.c b/bpf_progs/test.c
index 2b4a08f..fff3512 100644
--- a/bpf_progs/test.c
+++ b/bpf_progs/test.c
@@ -22,11 +22,11 @@
// BTF is incompatible with bpfloaders < v0.10, hence for S (v0.2) we must
// ship a different file than for later versions, but we need bpfloader v0.25+
// for obj@ver.o support
-#define BPFLOADER_MIN_VER BPFLOADER_OBJ_AT_VER_VERSION
+#define BPFLOADER_MIN_VER BPFLOADER_MAINLINE_T_VERSION
#else /* MAINLINE */
-// The resulting .o needs to load on the Android S & T bpfloaders
+// The resulting .o needs to load on the Android S bpfloader
#define BPFLOADER_MIN_VER BPFLOADER_S_VERSION
-#define BPFLOADER_MAX_VER BPFLOADER_OBJ_AT_VER_VERSION
+#define BPFLOADER_MAX_VER BPFLOADER_T_VERSION
#endif /* MAINLINE */
// Warning: values other than AID_ROOT don't work for map uid on BpfLoader < v0.21
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index a247bda..e9c6d8a 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -250,12 +250,38 @@
static int doLoad(char** argv, char * const envp[]) {
const bool runningAsRoot = !getuid(); // true iff U QPR3 or V+
- const bool unreleased = (base::GetProperty("ro.build.version.codename", "") != "REL");
- const int device_api_level = android_get_device_api_level() + (int)unreleased;
- const bool isAtLeastT = (device_api_level >= __ANDROID_API_T__);
- const bool isAtLeastU = (device_api_level >= __ANDROID_API_U__);
- const bool isAtLeastV = (device_api_level >= __ANDROID_API_V__);
+ // Any released device will have codename REL instead of a 'real' codename.
+ // For safety: default to 'REL' so we default to unreleased=false on failure.
+ const bool unreleased = (base::GetProperty("ro.build.version.codename", "REL") != "REL");
+
+ // goog/main device_api_level is bumped *way* before aosp/main api level
+ // (the latter only gets bumped during the push of goog/main to aosp/main)
+ //
+ // Since we develop in AOSP, we want it to behave as if it was bumped too.
+ //
+ // Note that AOSP doesn't really have a good api level (for example during
+ // early V dev cycle, it would have *all* of T, some but not all of U, and some V).
+ // One could argue that for our purposes AOSP api level should be infinite or 10000.
+ //
+ // This could also cause api to be increased in goog/main or other branches,
+ // but I can't imagine a case where this would be a problem: the problem
+ // is rather a too low api level, rather than some ill defined high value.
+ // For example as I write this aosp is 34/U, and goog is 35/V,
+ // we want to treat both goog & aosp as 35/V, but it's harmless if we
+ // treat goog as 36 because that value isn't yet defined to mean anything,
+ // and we thus never compare against it.
+ //
+ // Also note that 'android_get_device_api_level()' is what the
+ // //system/core/init/apex_init_util.cpp
+ // apex init .XXrc parsing code uses for XX filtering.
+ //
+ // That code has a hack to bump <35 to 35 (to force aosp/main to parse .35rc),
+ // but could (should?) perhaps be adjusted to match this.
+ const int effective_api_level = android_get_device_api_level() + (int)unreleased;
+ const bool isAtLeastT = (effective_api_level >= __ANDROID_API_T__);
+ const bool isAtLeastU = (effective_api_level >= __ANDROID_API_U__);
+ const bool isAtLeastV = (effective_api_level >= __ANDROID_API_V__);
// last in U QPR2 beta1
const bool has_platform_bpfloader_rc = exists("/system/etc/init/bpfloader.rc");
@@ -270,7 +296,7 @@
if (isAtLeastV) ++bpfloader_ver; // [46] BPFLOADER_MAINLINE_V_VERSION
ALOGI("NetBpfLoad v0.%u (%s) api:%d/%d kver:%07x (%s) uid:%d rc:%d%d",
- bpfloader_ver, argv[0], android_get_device_api_level(), device_api_level,
+ bpfloader_ver, argv[0], android_get_device_api_level(), effective_api_level,
kernelVersion(), describeArch(), getuid(),
has_platform_bpfloader_rc, has_platform_netbpfload_rc);
diff --git a/netbpfload/loader.cpp b/netbpfload/loader.cpp
index 289b4d7..72ff172 100644
--- a/netbpfload/loader.cpp
+++ b/netbpfload/loader.cpp
@@ -1122,11 +1122,20 @@
readSectionUint("bpfloader_max_ver", elfFile, DEFAULT_BPFLOADER_MAX_VER);
unsigned int bpfLoaderMinRequiredVer =
readSectionUint("bpfloader_min_required_ver", elfFile, 0);
+ unsigned int netBpfLoadMinVer =
+ readSectionUint("netbpfload_min_ver", elfFile, 0);
size_t sizeOfBpfMapDef =
readSectionUint("size_of_bpf_map_def", elfFile, DEFAULT_SIZEOF_BPF_MAP_DEF);
size_t sizeOfBpfProgDef =
readSectionUint("size_of_bpf_prog_def", elfFile, DEFAULT_SIZEOF_BPF_PROG_DEF);
+ // temporary hack to enable gentle enablement of mainline NetBpfLoad
+ if (bpfloader_ver < netBpfLoadMinVer) {
+ ALOGI("NetBpfLoad version %d ignoring ELF object %s with netbpfload min ver %d",
+ bpfloader_ver, elfPath, netBpfLoadMinVer);
+ return 0;
+ }
+
// inclusive lower bound check
if (bpfloader_ver < bpfLoaderMinVer) {
ALOGI("BpfLoader version 0x%05x ignoring ELF object %s with min ver 0x%05x",
diff --git a/netbpfload/netbpfload.33rc b/netbpfload/netbpfload.33rc
index 05f566e..d269ce9 100644
--- a/netbpfload/netbpfload.33rc
+++ b/netbpfload/netbpfload.33rc
@@ -1,3 +1,15 @@
+# This file takes effect only on T and U (on V netbpfload.35rc takes priority).
+#
+# The service is started from netd's libnetd_updatable shared library
+# on initial (boot time) startup of netd.
+#
+# However we never start this service on U QPR3.
+#
+# This is due to lack of a need: U QPR2 split the previously single
+# platform bpfloader into platform netbpfload -> platform bpfloader.
+# U QPR3 made the platform netbpfload unconditionally exec apex netbpfload,
+# so by the time U QPR3's netd runs, apex netbpfload is already done.
+
service mdnsd_netbpfload /apex/com.android.tethering/bin/netbpfload
capabilities CHOWN SYS_ADMIN NET_ADMIN
group system root graphics network_stack net_admin net_bw_acct net_bw_stats net_raw
@@ -5,5 +17,5 @@
file /dev/kmsg w
rlimit memlock 1073741824 1073741824
oneshot
- reboot_on_failure reboot,bpfloader-failed
+ # TODO: reboot_on_failure reboot,netbpfload-failed
override
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index d50d6c2..f884beb 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -170,16 +170,25 @@
android::bpf::waitForProgsLoaded();
}
- if (false && !mainlineNetBpfLoadDone()) {
- // we're on < U QPR3 & it's the first time netd is starting up (unless crashlooping)
+ if (!mainlineNetBpfLoadDone()) {
+ const bool enforce_mainline = false; // TODO: flip to true
+
+ // We're on < U QPR3 & it's the first time netd is starting up (unless crashlooping)
+ //
+ // On U QPR3+ netbpfload is guaranteed to run before the platform bpfloader,
+ // so waitForProgsLoaded() implies mainlineNetBpfLoadDone().
if (!base::SetProperty("ctl.start", "mdnsd_netbpfload")) {
ALOGE("Failed to set property ctl.start=mdnsd_netbpfload, see dmesg for reason.");
- abort();
+ if (enforce_mainline) abort();
}
- ALOGI("Waiting for Networking BPF programs");
- waitForNetProgsLoaded();
- ALOGI("Networking BPF programs are loaded");
+ if (enforce_mainline) {
+ ALOGI("Waiting for Networking BPF programs");
+ waitForNetProgsLoaded();
+ ALOGI("Networking BPF programs are loaded");
+ } else {
+ ALOGI("Started mdnsd_netbpfload asynchronously.");
+ }
}
ALOGI("BPF programs are loaded");
@@ -190,7 +199,30 @@
return netdutils::status::ok;
}
+static void mapLockTest(void) {
+ // The maps must be R/W, and as yet unopened (or more specifically not yet lock'ed).
+ const char * const m1 = BPF_NETD_PATH "map_netd_lock_array_test_map";
+ const char * const m2 = BPF_NETD_PATH "map_netd_lock_hash_test_map";
+
+ unique_fd fd0(bpf::mapRetrieveExclusiveRW(m1)); if (!fd0.ok()) abort(); // grabs exclusive lock
+
+ unique_fd fd1(bpf::mapRetrieveExclusiveRW(m2)); if (!fd1.ok()) abort(); // no conflict with fd0
+ unique_fd fd2(bpf::mapRetrieveExclusiveRW(m2)); if ( fd2.ok()) abort(); // busy due to fd1
+ unique_fd fd3(bpf::mapRetrieveRO(m2)); if (!fd3.ok()) abort(); // no lock taken
+ unique_fd fd4(bpf::mapRetrieveRW(m2)); if ( fd4.ok()) abort(); // busy due to fd1
+ fd1.reset(); // releases exclusive lock
+ unique_fd fd5(bpf::mapRetrieveRO(m2)); if (!fd5.ok()) abort(); // no lock taken
+ unique_fd fd6(bpf::mapRetrieveRW(m2)); if (!fd6.ok()) abort(); // now ok
+ unique_fd fd7(bpf::mapRetrieveRO(m2)); if (!fd7.ok()) abort(); // no lock taken
+ unique_fd fd8(bpf::mapRetrieveExclusiveRW(m2)); if ( fd8.ok()) abort(); // busy due to fd6
+
+ fd0.reset(); // releases exclusive lock
+ unique_fd fd9(bpf::mapRetrieveWO(m1)); if (!fd9.ok()) abort(); // grabs exclusive lock
+}
+
Status BpfHandler::initMaps() {
+ mapLockTest();
+
RETURN_IF_NOT_OK(mStatsMapA.init(STATS_MAP_A_PATH));
RETURN_IF_NOT_OK(mStatsMapB.init(STATS_MAP_B_PATH));
RETURN_IF_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH));
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 18a274a..c0082bb 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -80,7 +80,8 @@
case VERIFY_BIN: return;
case VERIFY_PROG: fd = bpf::retrieveProgram(path); break;
case VERIFY_MAP_RO: fd = bpf::mapRetrieveRO(path); break;
- case VERIFY_MAP_RW: fd = bpf::mapRetrieveRW(path); break;
+ // lockless: we're just checking access rights and will immediately close the fd
+ case VERIFY_MAP_RW: fd = bpf::mapRetrieveLocklessRW(path); break;
}
if (fd < 0) ALOGF("bpf_obj_get '%s' failed, errno=%d", path, errno);
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index ca2cb15..ba059ab 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -109,6 +109,7 @@
import static android.os.Process.INVALID_UID;
import static android.os.Process.VPN_UID;
import static android.system.OsConstants.ETH_P_ALL;
+import static android.system.OsConstants.F_OK;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -268,6 +269,7 @@
import android.stats.connectivity.ValidatedState;
import android.sysprop.NetworkProperties;
import android.system.ErrnoException;
+import android.system.Os;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -1834,6 +1836,28 @@
new PermissionMonitor(mContext, mNetd, mBpfNetMaps, mHandlerThread);
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
+ // Temporary hack to report netbpfload result.
+ // TODO: remove in 2024-09 when netbpfload starts loading mainline bpf programs.
+ mHandler.postDelayed(() -> {
+ // Did netbpfload create the map?
+ try {
+ Os.access("/sys/fs/bpf/net_shared/map_gentle_test", F_OK);
+ } catch (ErrnoException e) {
+ Log.wtf(TAG, "netbpfload did not create map", e);
+ }
+ // Did netbpfload create the program?
+ try {
+ Os.access("/sys/fs/bpf/net_shared/prog_gentle_skfilter_accept", F_OK);
+ } catch (ErrnoException e) {
+ Log.wtf(TAG, "netbpfload did not create program", e);
+ }
+ // Did netbpfload run to completion?
+ try {
+ Os.access("/sys/fs/bpf/netd_shared/mainline_done", F_OK);
+ } catch (ErrnoException e) {
+ Log.wtf(TAG, "netbpfload did not run to completion", e);
+ }
+ }, 30_000 /* delayMillis */);
mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
mConnectivityDiagnosticsHandler =
new ConnectivityDiagnosticsHandler(mHandlerThread.getLooper());
diff --git a/staticlibs/device/com/android/net/module/util/BpfMap.java b/staticlibs/device/com/android/net/module/util/BpfMap.java
index da77ae8..dafff20 100644
--- a/staticlibs/device/com/android/net/module/util/BpfMap.java
+++ b/staticlibs/device/com/android/net/module/util/BpfMap.java
@@ -52,6 +52,9 @@
public static final int BPF_F_RDONLY = 1 << 3;
public static final int BPF_F_WRONLY = 1 << 4;
+ // magic value for jni consumption, invalid from kernel point of view
+ public static final int BPF_F_RDWR_EXCLUSIVE = BPF_F_RDONLY | BPF_F_WRONLY;
+
public static final int BPF_MAP_TYPE_HASH = 1;
private static final int BPF_F_NO_PREALLOC = 1;
diff --git a/staticlibs/device/com/android/net/module/util/SingleWriterBpfMap.java b/staticlibs/device/com/android/net/module/util/SingleWriterBpfMap.java
index 3eb59d8..bbd921d 100644
--- a/staticlibs/device/com/android/net/module/util/SingleWriterBpfMap.java
+++ b/staticlibs/device/com/android/net/module/util/SingleWriterBpfMap.java
@@ -61,14 +61,9 @@
// our code can contain hundreds of items.
private final HashMap<K, V> mCache = new HashMap<>();
- protected SingleWriterBpfMap(@NonNull final String path, final int flag, final Class<K> key,
+ public SingleWriterBpfMap(@NonNull final String path, final Class<K> key,
final Class<V> value) throws ErrnoException, NullPointerException {
- super(path, flag, key, value);
-
- if (flag != BPF_F_RDWR) {
- throw new IllegalArgumentException(
- "Using " + getClass().getName() + " for read-only maps does not make sense");
- }
+ super(path, BPF_F_RDWR_EXCLUSIVE, key, value);
// Populate cache with the current map contents.
K currentKey = super.getFirstKey();
@@ -78,11 +73,6 @@
}
}
- public SingleWriterBpfMap(@NonNull final String path, final Class<K> key,
- final Class<V> value) throws ErrnoException, NullPointerException {
- this(path, BPF_F_RDWR, key, value);
- }
-
@Override
public synchronized void updateEntry(K key, V value) throws ErrnoException {
super.updateEntry(key, value);
diff --git a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
index d716358..cd51004 100644
--- a/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
+++ b/staticlibs/native/bpf_headers/include/bpf/BpfRingbuf.h
@@ -151,7 +151,7 @@
inline base::Result<void> BpfRingbufBase::Init(const char* path) {
- mRingFd.reset(mapRetrieveRW(path));
+ mRingFd.reset(mapRetrieveExclusiveRW(path));
if (!mRingFd.ok()) {
return android::base::ErrnoError()
<< "failed to retrieve ringbuffer at " << path;
diff --git a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
index 6864bad..9bd6470 100644
--- a/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
+++ b/staticlibs/native/bpf_headers/include/bpf/bpf_helpers.h
@@ -117,6 +117,9 @@
unsigned _btf_min_bpfloader_ver SECTION("btf_min_bpfloader_ver") = 39u; \
unsigned _btf_user_min_bpfloader_ver SECTION("btf_user_min_bpfloader_ver") = 0xFFFFFFFFu
+#define DISABLE_ON_MAINLINE_BEFORE_U_QPR3() \
+ unsigned _netbpfload_min_ver SECTION("netbpfload_min_ver") = BPFLOADER_MAINLINE_U_QPR3_VERSION;
+
/* flag the resulting bpf .o file as critical to system functionality,
* loading all kernel version appropriate programs in it must succeed
* for bpfloader success
diff --git a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
index 2a0e8e0..73cef89 100644
--- a/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
+++ b/staticlibs/native/bpf_syscall_wrappers/include/BpfSyscallWrappers.h
@@ -16,8 +16,11 @@
#pragma once
+#include <stdlib.h>
+#include <unistd.h>
#include <linux/bpf.h>
#include <linux/unistd.h>
+#include <sys/file.h>
#ifdef BPF_FD_JUST_USE_INT
#define BPF_FD_TYPE int
@@ -128,16 +131,61 @@
});
}
-inline int mapRetrieveRW(const char* pathname) {
+int bpfGetFdMapId(const BPF_FD_TYPE map_fd);
+
+inline int bpfLock(int fd, short type) {
+ if (fd < 0) return fd; // pass any errors straight through
+#ifdef BPF_MAP_LOCKLESS_FOR_TEST
+ return fd;
+#endif
+#ifdef BPF_FD_JUST_USE_INT
+ int mapId = bpfGetFdMapId(fd);
+ int saved_errno = errno;
+#else
+ base::unique_fd ufd(fd);
+ int mapId = bpfGetFdMapId(ufd);
+ int saved_errno = errno;
+ (void)ufd.release();
+#endif
+ // 4.14+ required to fetch map id, but we don't want to call isAtLeastKernelVersion
+ if (mapId == -1 && saved_errno == EINVAL) return fd;
+ if (mapId <= 0) abort(); // should not be possible
+
+ // on __LP64__ (aka. 64-bit userspace) 'struct flock64' is the same as 'struct flock'
+ struct flock64 fl = {
+ .l_type = type, // short: F_{RD,WR,UN}LCK
+ .l_whence = SEEK_SET, // short: SEEK_{SET,CUR,END}
+ .l_start = mapId, // off_t: start offset
+ .l_len = 1, // off_t: number of bytes
+ };
+
+ // see: bionic/libc/bionic/fcntl.cpp: iff !__LP64__ this uses fcntl64
+ int ret = fcntl(fd, F_OFD_SETLK, &fl);
+ if (!ret) return fd; // success
+ close(fd);
+ return ret; // most likely -1 with errno == EAGAIN, due to already held lock
+}
+
+inline int mapRetrieveLocklessRW(const char* pathname) {
return bpfFdGet(pathname, 0);
}
+inline int mapRetrieveExclusiveRW(const char* pathname) {
+ return bpfLock(mapRetrieveLocklessRW(pathname), F_WRLCK);
+}
+
+inline int mapRetrieveRW(const char* pathname) {
+ return bpfLock(mapRetrieveLocklessRW(pathname), F_RDLCK);
+}
+
inline int mapRetrieveRO(const char* pathname) {
return bpfFdGet(pathname, BPF_F_RDONLY);
}
+// WARNING: it's impossible to grab a shared (ie. read) lock on a write-only fd,
+// so we instead choose to grab an exclusive (ie. write) lock.
inline int mapRetrieveWO(const char* pathname) {
- return bpfFdGet(pathname, BPF_F_WRONLY);
+ return bpfLock(bpfFdGet(pathname, BPF_F_WRONLY), F_WRLCK);
}
inline int retrieveProgram(const char* pathname) {
diff --git a/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp b/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
index b92f107..1923ceb 100644
--- a/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
+++ b/staticlibs/native/bpfmapjni/com_android_net_module_util_BpfMap.cpp
@@ -35,7 +35,24 @@
jstring path, jint mode, jint keySize, jint valueSize) {
ScopedUtfChars pathname(env, path);
- jint fd = bpf::bpfFdGet(pathname.c_str(), static_cast<unsigned>(mode));
+ jint fd = -1;
+ switch (mode) {
+ case 0:
+ fd = bpf::mapRetrieveRW(pathname.c_str());
+ break;
+ case BPF_F_RDONLY:
+ fd = bpf::mapRetrieveRO(pathname.c_str());
+ break;
+ case BPF_F_WRONLY:
+ fd = bpf::mapRetrieveWO(pathname.c_str());
+ break;
+ case BPF_F_RDONLY|BPF_F_WRONLY:
+ fd = bpf::mapRetrieveExclusiveRW(pathname.c_str());
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
if (fd < 0) {
jniThrowErrnoException(env, "nativeBpfFdGet", errno);
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index 51a4eca..dca9dd7 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -91,6 +91,8 @@
SHARED "map_dscpPolicy_ipv4_dscp_policies_map",
SHARED "map_dscpPolicy_ipv6_dscp_policies_map",
SHARED "map_dscpPolicy_socket_policy_cache_map",
+ SHARED "map_gentle_test",
+ SHARED "prog_gentle_skfilter_accept",
NETD "map_netd_app_uid_stats_map",
NETD "map_netd_configuration_map",
NETD "map_netd_cookie_tag_map",
diff --git a/tests/native/utilities/firewall.h b/tests/native/utilities/firewall.h
index b3d69bf..a5cb0b9 100644
--- a/tests/native/utilities/firewall.h
+++ b/tests/native/utilities/firewall.h
@@ -18,6 +18,7 @@
#pragma once
#include <android-base/thread_annotations.h>
+#define BPF_MAP_LOCKLESS_FOR_TEST
#include <bpf/BpfMap.h>
#include "netd.h"