[NETD-BPF#15] Move BPF map definition and utils to frameworks/libs/net/

1. Move BPF map definition and utilities to a common place that easy to
be referenced from both mainline module and platform code.

2. Create cc_library_headers bpf_map_utils which includes BPF map
definition and utils.

Bug: 202086915
Test: m; flash; boot
Test: cd system/netd/ && atest
Test: cd packages/modules/Connectivity && atest
Test: m gpuservice_unittest libtimeinstate_test bpf_module_test
      CtsAppOpsTestCases libbpf_load_test VtsBootconfigTest
      vts_test_binary_bpf_module bpf_benchmark libbpf_load_test
      libbpf_android_test
Change-Id: I4d54b0d69029f593b8da3099d7ae16279692dabd
diff --git a/staticlibs/native/bpf_map_utils/Android.bp b/staticlibs/native/bpf_map_utils/Android.bp
new file mode 100644
index 0000000..521b682
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/Android.bp
@@ -0,0 +1,73 @@
+// 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.
+
+cc_library_headers {
+    name: "bpf_map_utils",
+    vendor_available: true,
+    host_supported: true,
+    native_bridge_supported: true,
+    export_include_dirs: ["include"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    min_sdk_version: "30",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.art.debug",
+    ],
+    visibility: [
+        "//bootable/libbootloader/vts",
+        "//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/tests/unit/jni",
+        "//packages/modules/DnsResolver/tests",
+        "//system/bpf/bpfloader",
+        "//system/bpf/libbpf_android",
+        "//system/memory/libmeminfo",
+        "//system/netd/libnetdbpf",
+        "//system/netd/server",
+        "//system/netd/tests",
+        "//system/netd/tests/benchmarks",
+        "//test/vts-testcase/kernel/api/bpf_native_test",
+    ],
+}
+
+
+cc_test {
+    // TODO: Rename to bpf_map_test and modify .gcls as well.
+    name: "libbpf_android_test",
+    srcs: [
+        "BpfMapTest.cpp",
+    ],
+    defaults: ["bpf_defaults"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-error=unused-variable",
+    ],
+    static_libs: ["libgmock"],
+    shared_libs: [
+        "libbpf_android",
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+    require_root: true,
+    test_suites: ["general-tests"],
+}
diff --git a/staticlibs/native/bpf_map_utils/BpfMapTest.cpp b/staticlibs/native/bpf_map_utils/BpfMapTest.cpp
new file mode 100644
index 0000000..d0737b0
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/BpfMapTest.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2018 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 <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/inet_diag.h>
+#include <linux/sock_diag.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "bpf/BpfMap.h"
+#include "bpf/BpfUtils.h"
+
+using ::testing::Test;
+
+namespace android {
+namespace bpf {
+
+using base::Result;
+using base::unique_fd;
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint32_t TEST_KEY1 = 1;
+constexpr uint32_t TEST_VALUE1 = 10;
+constexpr const char PINNED_MAP_PATH[] = "/sys/fs/bpf/testMap";
+
+class BpfMapTest : public testing::Test {
+  protected:
+    BpfMapTest() {}
+
+    void SetUp() {
+        EXPECT_EQ(0, setrlimitForTest());
+        if (!access(PINNED_MAP_PATH, R_OK)) {
+            EXPECT_EQ(0, remove(PINNED_MAP_PATH));
+        }
+    }
+
+    void TearDown() {
+        if (!access(PINNED_MAP_PATH, R_OK)) {
+            EXPECT_EQ(0, remove(PINNED_MAP_PATH));
+        }
+    }
+
+    void checkMapInvalid(BpfMap<uint32_t, uint32_t>& map) {
+        EXPECT_FALSE(map.isValid());
+        EXPECT_EQ(-1, map.getMap().get());
+    }
+
+    void checkMapValid(BpfMap<uint32_t, uint32_t>& map) {
+        EXPECT_LE(0, map.getMap().get());
+        EXPECT_TRUE(map.isValid());
+    }
+
+    void writeToMapAndCheck(BpfMap<uint32_t, uint32_t>& map, uint32_t key, uint32_t value) {
+        ASSERT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
+        uint32_t value_read;
+        ASSERT_EQ(0, findMapEntry(map.getMap(), &key, &value_read));
+        checkValueAndStatus(value, value_read);
+    }
+
+    void checkValueAndStatus(uint32_t refValue, Result<uint32_t> value) {
+        ASSERT_RESULT_OK(value);
+        ASSERT_EQ(refValue, value.value());
+    }
+
+    void populateMap(uint32_t total, BpfMap<uint32_t, uint32_t>& map) {
+        for (uint32_t key = 0; key < total; key++) {
+            uint32_t value = key * 10;
+            EXPECT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
+        }
+    }
+
+    void expectMapEmpty(BpfMap<uint32_t, uint32_t>& map) {
+        Result<bool> isEmpty = map.isEmpty();
+        ASSERT_RESULT_OK(isEmpty);
+        ASSERT_TRUE(isEmpty.value());
+    }
+};
+
+TEST_F(BpfMapTest, constructor) {
+    BpfMap<uint32_t, uint32_t> testMap1;
+    checkMapInvalid(testMap1);
+
+    BpfMap<uint32_t, uint32_t> testMap2(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    checkMapValid(testMap2);
+}
+
+TEST_F(BpfMapTest, basicHelpers) {
+    BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    uint32_t key = TEST_KEY1;
+    uint32_t value_write = TEST_VALUE1;
+    writeToMapAndCheck(testMap, key, value_write);
+    Result<uint32_t> value_read = testMap.readValue(key);
+    checkValueAndStatus(value_write, value_read);
+    Result<uint32_t> key_read = testMap.getFirstKey();
+    checkValueAndStatus(key, key_read);
+    ASSERT_RESULT_OK(testMap.deleteValue(key));
+    ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_read));
+    ASSERT_EQ(ENOENT, errno);
+}
+
+TEST_F(BpfMapTest, reset) {
+    BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    uint32_t key = TEST_KEY1;
+    uint32_t value_write = TEST_VALUE1;
+    writeToMapAndCheck(testMap, key, value_write);
+
+    testMap.reset(-1);
+    checkMapInvalid(testMap);
+    ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_write));
+    ASSERT_EQ(EBADF, errno);
+}
+
+TEST_F(BpfMapTest, moveConstructor) {
+    BpfMap<uint32_t, uint32_t> testMap1(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    BpfMap<uint32_t, uint32_t> testMap2;
+    testMap2 = std::move(testMap1);
+    uint32_t key = TEST_KEY1;
+    checkMapInvalid(testMap1);
+    uint32_t value = TEST_VALUE1;
+    writeToMapAndCheck(testMap2, key, value);
+}
+
+TEST_F(BpfMapTest, SetUpMap) {
+    EXPECT_NE(0, access(PINNED_MAP_PATH, R_OK));
+    BpfMap<uint32_t, uint32_t> testMap1(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    ASSERT_EQ(0, bpfFdPin(testMap1.getMap(), PINNED_MAP_PATH));
+    EXPECT_EQ(0, access(PINNED_MAP_PATH, R_OK));
+    checkMapValid(testMap1);
+    BpfMap<uint32_t, uint32_t> testMap2;
+    EXPECT_RESULT_OK(testMap2.init(PINNED_MAP_PATH));
+    checkMapValid(testMap2);
+    uint32_t key = TEST_KEY1;
+    uint32_t value = TEST_VALUE1;
+    writeToMapAndCheck(testMap1, key, value);
+    Result<uint32_t> value_read = testMap2.readValue(key);
+    checkValueAndStatus(value, value_read);
+}
+
+TEST_F(BpfMapTest, iterate) {
+    BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    populateMap(TEST_MAP_SIZE, testMap);
+    int totalCount = 0;
+    int totalSum = 0;
+    const auto iterateWithDeletion = [&totalCount, &totalSum](const uint32_t& key,
+                                                              BpfMap<uint32_t, uint32_t>& map) {
+        EXPECT_GE((uint32_t)TEST_MAP_SIZE, key);
+        totalCount++;
+        totalSum += key;
+        return map.deleteValue(key);
+    };
+    EXPECT_RESULT_OK(testMap.iterate(iterateWithDeletion));
+    EXPECT_EQ((int)TEST_MAP_SIZE, totalCount);
+    EXPECT_EQ(((1 + TEST_MAP_SIZE - 1) * (TEST_MAP_SIZE - 1)) / 2, (uint32_t)totalSum);
+    expectMapEmpty(testMap);
+}
+
+TEST_F(BpfMapTest, iterateWithValue) {
+    BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    populateMap(TEST_MAP_SIZE, testMap);
+    int totalCount = 0;
+    int totalSum = 0;
+    const auto iterateWithDeletion = [&totalCount, &totalSum](const uint32_t& key,
+                                                              const uint32_t& value,
+                                                              BpfMap<uint32_t, uint32_t>& map) {
+        EXPECT_GE((uint32_t)TEST_MAP_SIZE, key);
+        EXPECT_EQ(value, key * 10);
+        totalCount++;
+        totalSum += value;
+        return map.deleteValue(key);
+    };
+    EXPECT_RESULT_OK(testMap.iterateWithValue(iterateWithDeletion));
+    EXPECT_EQ((int)TEST_MAP_SIZE, totalCount);
+    EXPECT_EQ(((1 + TEST_MAP_SIZE - 1) * (TEST_MAP_SIZE - 1)) * 5, (uint32_t)totalSum);
+    expectMapEmpty(testMap);
+}
+
+TEST_F(BpfMapTest, mapIsEmpty) {
+    BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    expectMapEmpty(testMap);
+    uint32_t key = TEST_KEY1;
+    uint32_t value_write = TEST_VALUE1;
+    writeToMapAndCheck(testMap, key, value_write);
+    Result<bool> isEmpty = testMap.isEmpty();
+    ASSERT_RESULT_OK(isEmpty);
+    ASSERT_FALSE(isEmpty.value());
+    ASSERT_RESULT_OK(testMap.deleteValue(key));
+    ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_write));
+    ASSERT_EQ(ENOENT, errno);
+    expectMapEmpty(testMap);
+    int entriesSeen = 0;
+    EXPECT_RESULT_OK(testMap.iterate(
+            [&entriesSeen](const unsigned int&,
+                           const BpfMap<unsigned int, unsigned int>&) -> Result<void> {
+                entriesSeen++;
+                return {};
+            }));
+    EXPECT_EQ(0, entriesSeen);
+    EXPECT_RESULT_OK(testMap.iterateWithValue(
+            [&entriesSeen](const unsigned int&, const unsigned int&,
+                           const BpfMap<unsigned int, unsigned int>&) -> Result<void> {
+                entriesSeen++;
+                return {};
+            }));
+    EXPECT_EQ(0, entriesSeen);
+}
+
+TEST_F(BpfMapTest, mapClear) {
+    BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+    populateMap(TEST_MAP_SIZE, testMap);
+    Result<bool> isEmpty = testMap.isEmpty();
+    ASSERT_RESULT_OK(isEmpty);
+    ASSERT_FALSE(*isEmpty);
+    ASSERT_RESULT_OK(testMap.clear());
+    expectMapEmpty(testMap);
+}
+
+}  // namespace bpf
+}  // namespace android
diff --git a/staticlibs/native/bpf_map_utils/TEST_MAPPING b/staticlibs/native/bpf_map_utils/TEST_MAPPING
new file mode 100644
index 0000000..9ec8a40
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libbpf_android_test"
+    }
+  ]
+}
diff --git a/staticlibs/native/bpf_map_utils/include/bpf/BpfMap.h b/staticlibs/native/bpf_map_utils/include/bpf/BpfMap.h
new file mode 100644
index 0000000..bdffc0f
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/include/bpf/BpfMap.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 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 <linux/bpf.h>
+
+#include <android-base/result.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <utils/Log.h>
+#include "bpf/BpfUtils.h"
+
+namespace android {
+namespace bpf {
+
+// This is a class wrapper for eBPF maps. The eBPF map is a special in-kernel
+// data structure that stores data in <Key, Value> pairs. It can be read/write
+// from userspace by passing syscalls with the map file descriptor. This class
+// is used to generalize the procedure of interacting with eBPF maps and hide
+// the implementation detail from other process. Besides the basic syscalls
+// wrapper, it also provides some useful helper functions as well as an iterator
+// nested class to iterate the map more easily.
+//
+// NOTE: A kernel eBPF map may be accessed by both kernel and userspace
+// processes at the same time. Or if the map is pinned as a virtual file, it can
+// be obtained by multiple eBPF map class object and accessed concurrently.
+// Though the map class object and the underlying kernel map are thread safe, it
+// is not safe to iterate over a map while another thread or process is deleting
+// from it. In this case the iteration can return duplicate entries.
+template <class Key, class Value>
+class BpfMap {
+  public:
+    BpfMap<Key, Value>() {};
+
+  protected:
+    // flag must be within BPF_OBJ_FLAG_MASK, ie. 0, BPF_F_RDONLY, BPF_F_WRONLY
+    BpfMap<Key, Value>(const char* pathname, uint32_t flags) {
+        int map_fd = mapRetrieve(pathname, flags);
+        if (map_fd >= 0) mMapFd.reset(map_fd);
+    }
+
+  public:
+    explicit BpfMap<Key, Value>(const char* pathname) : BpfMap<Key, Value>(pathname, 0) {}
+
+    BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) {
+        int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags);
+        if (map_fd >= 0) mMapFd.reset(map_fd);
+    }
+
+    base::Result<Key> getFirstKey() const {
+        Key firstKey;
+        if (getFirstMapKey(mMapFd, &firstKey)) {
+            return ErrnoErrorf("Get firstKey map {} failed", mMapFd.get());
+        }
+        return firstKey;
+    }
+
+    base::Result<Key> getNextKey(const Key& key) const {
+        Key nextKey;
+        if (getNextMapKey(mMapFd, &key, &nextKey)) {
+            return ErrnoErrorf("Get next key of map {} failed", mMapFd.get());
+        }
+        return nextKey;
+    }
+
+    base::Result<void> writeValue(const Key& key, const Value& value, uint64_t flags) {
+        if (writeToMapEntry(mMapFd, &key, &value, flags)) {
+            return ErrnoErrorf("Write to map {} failed", mMapFd.get());
+        }
+        return {};
+    }
+
+    base::Result<Value> readValue(const Key key) const {
+        Value value;
+        if (findMapEntry(mMapFd, &key, &value)) {
+            return ErrnoErrorf("Read value of map {} failed", mMapFd.get());
+        }
+        return value;
+    }
+
+    base::Result<void> deleteValue(const Key& key) {
+        if (deleteMapEntry(mMapFd, &key)) {
+            return ErrnoErrorf("Delete entry from map {} failed", mMapFd.get());
+        }
+        return {};
+    }
+
+    // Function that tries to get map from a pinned path.
+    base::Result<void> init(const char* path);
+
+    // Iterate through the map and handle each key retrieved based on the filter
+    // without modification of map content.
+    base::Result<void> iterate(
+            const std::function<base::Result<void>(const Key& key, const BpfMap<Key, Value>& map)>&
+                    filter) const;
+
+    // Iterate through the map and get each <key, value> pair, handle each <key,
+    // value> pair based on the filter without modification of map content.
+    base::Result<void> iterateWithValue(
+            const std::function<base::Result<void>(const Key& key, const Value& value,
+                                                   const BpfMap<Key, Value>& map)>& filter) const;
+
+    // Iterate through the map and handle each key retrieved based on the filter
+    base::Result<void> iterate(
+            const std::function<base::Result<void>(const Key& key, BpfMap<Key, Value>& map)>&
+                    filter);
+
+    // Iterate through the map and get each <key, value> pair, handle each <key,
+    // value> pair based on the filter.
+    base::Result<void> iterateWithValue(
+            const std::function<base::Result<void>(const Key& key, const Value& value,
+                                                   BpfMap<Key, Value>& map)>& filter);
+
+    const base::unique_fd& getMap() const { return mMapFd; };
+
+    // Copy assignment operator
+    BpfMap<Key, Value>& operator=(const BpfMap<Key, Value>& other) {
+        if (this != &other) mMapFd.reset(fcntl(other.mMapFd.get(), F_DUPFD_CLOEXEC, 0));
+        return *this;
+    }
+
+    // Move assignment operator
+    BpfMap<Key, Value>& operator=(BpfMap<Key, Value>&& other) noexcept {
+        mMapFd = std::move(other.mMapFd);
+        other.reset(-1);
+        return *this;
+    }
+
+    void reset(base::unique_fd fd) = delete;
+
+    void reset(int fd) { mMapFd.reset(fd); }
+
+    bool isValid() const { return mMapFd != -1; }
+
+    base::Result<void> clear() {
+        while (true) {
+            auto key = getFirstKey();
+            if (!key.ok()) {
+                if (key.error().code() == ENOENT) return {};  // empty: success
+                return key.error();                           // Anything else is an error
+            }
+            auto res = deleteValue(key.value());
+            if (!res.ok()) {
+                // Someone else could have deleted the key, so ignore ENOENT
+                if (res.error().code() == ENOENT) continue;
+                ALOGE("Failed to delete data %s", strerror(res.error().code()));
+                return res.error();
+            }
+        }
+    }
+
+    base::Result<bool> isEmpty() const {
+        auto key = getFirstKey();
+        if (!key.ok()) {
+            // Return error code ENOENT means the map is empty
+            if (key.error().code() == ENOENT) return true;
+            return key.error();
+        }
+        return false;
+    }
+
+  private:
+    base::unique_fd mMapFd;
+};
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::init(const char* path) {
+    mMapFd = base::unique_fd(mapRetrieveRW(path));
+    if (mMapFd == -1) {
+        return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path);
+    }
+    return {};
+}
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::iterate(
+        const std::function<base::Result<void>(const Key& key, const BpfMap<Key, Value>& map)>&
+                filter) const {
+    base::Result<Key> curKey = getFirstKey();
+    while (curKey.ok()) {
+        const base::Result<Key>& nextKey = getNextKey(curKey.value());
+        base::Result<void> status = filter(curKey.value(), *this);
+        if (!status.ok()) return status;
+        curKey = nextKey;
+    }
+    if (curKey.error().code() == ENOENT) return {};
+    return curKey.error();
+}
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::iterateWithValue(
+        const std::function<base::Result<void>(const Key& key, const Value& value,
+                                               const BpfMap<Key, Value>& map)>& filter) const {
+    base::Result<Key> curKey = getFirstKey();
+    while (curKey.ok()) {
+        const base::Result<Key>& nextKey = getNextKey(curKey.value());
+        base::Result<Value> curValue = readValue(curKey.value());
+        if (!curValue.ok()) return curValue.error();
+        base::Result<void> status = filter(curKey.value(), curValue.value(), *this);
+        if (!status.ok()) return status;
+        curKey = nextKey;
+    }
+    if (curKey.error().code() == ENOENT) return {};
+    return curKey.error();
+}
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::iterate(
+        const std::function<base::Result<void>(const Key& key, BpfMap<Key, Value>& map)>& filter) {
+    base::Result<Key> curKey = getFirstKey();
+    while (curKey.ok()) {
+        const base::Result<Key>& nextKey = getNextKey(curKey.value());
+        base::Result<void> status = filter(curKey.value(), *this);
+        if (!status.ok()) return status;
+        curKey = nextKey;
+    }
+    if (curKey.error().code() == ENOENT) return {};
+    return curKey.error();
+}
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::iterateWithValue(
+        const std::function<base::Result<void>(const Key& key, const Value& value,
+                                               BpfMap<Key, Value>& map)>& filter) {
+    base::Result<Key> curKey = getFirstKey();
+    while (curKey.ok()) {
+        const base::Result<Key>& nextKey = getNextKey(curKey.value());
+        base::Result<Value> curValue = readValue(curKey.value());
+        if (!curValue.ok()) return curValue.error();
+        base::Result<void> status = filter(curKey.value(), curValue.value(), *this);
+        if (!status.ok()) return status;
+        curKey = nextKey;
+    }
+    if (curKey.error().code() == ENOENT) return {};
+    return curKey.error();
+}
+
+template <class Key, class Value>
+class BpfMapRO : public BpfMap<Key, Value> {
+  public:
+    explicit BpfMapRO<Key, Value>(const char* pathname)
+        : BpfMap<Key, Value>(pathname, BPF_F_RDONLY) {}
+};
+
+}  // namespace bpf
+}  // namespace android
diff --git a/staticlibs/native/bpf_map_utils/include/bpf/BpfUtils.h b/staticlibs/native/bpf_map_utils/include/bpf/BpfUtils.h
new file mode 100644
index 0000000..265d4b6
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/include/bpf/BpfUtils.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 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 <linux/if_ether.h>
+#include <linux/pfkeyv2.h>
+#include <net/if.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+
+#include "BpfSyscallWrappers.h"
+
+// The buffer size for the buffer that records program loading logs, needs to be large enough for
+// the largest kernel program.
+
+namespace android {
+namespace bpf {
+
+constexpr const int OVERFLOW_COUNTERSET = 2;
+
+constexpr const uint64_t NONEXISTENT_COOKIE = 0;
+
+static inline uint64_t getSocketCookie(int sockFd) {
+    uint64_t sock_cookie;
+    socklen_t cookie_len = sizeof(sock_cookie);
+    int res = getsockopt(sockFd, SOL_SOCKET, SO_COOKIE, &sock_cookie, &cookie_len);
+    if (res < 0) {
+        res = -errno;
+        ALOGE("Failed to get socket cookie: %s\n", strerror(errno));
+        errno = -res;
+        // 0 is an invalid cookie. See sock_gen_cookie.
+        return NONEXISTENT_COOKIE;
+    }
+    return sock_cookie;
+}
+
+static inline int synchronizeKernelRCU() {
+    // This is a temporary hack for network stats map swap on devices running
+    // 4.9 kernels. The kernel code of socket release on pf_key socket will
+    // explicitly call synchronize_rcu() which is exactly what we need.
+    int pfSocket = socket(AF_KEY, SOCK_RAW | SOCK_CLOEXEC, PF_KEY_V2);
+
+    if (pfSocket < 0) {
+        int ret = -errno;
+        ALOGE("create PF_KEY socket failed: %s", strerror(errno));
+        return ret;
+    }
+
+    // When closing socket, synchronize_rcu() gets called in sock_release().
+    if (close(pfSocket)) {
+        int ret = -errno;
+        ALOGE("failed to close the PF_KEY socket: %s", strerror(errno));
+        return ret;
+    }
+    return 0;
+}
+
+static inline int setrlimitForTest() {
+    // Set the memory rlimit for the test process if the default MEMLOCK rlimit is not enough.
+    struct rlimit limit = {
+            .rlim_cur = 1073741824,  // 1 GiB
+            .rlim_max = 1073741824,  // 1 GiB
+    };
+    int res = setrlimit(RLIMIT_MEMLOCK, &limit);
+    if (res) {
+        ALOGE("Failed to set the default MEMLOCK rlimit: %s", strerror(errno));
+    }
+    return res;
+}
+
+#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
+
+static inline unsigned kernelVersion() {
+    struct utsname buf;
+    int ret = uname(&buf);
+    if (ret) return 0;
+
+    unsigned kver_major;
+    unsigned kver_minor;
+    unsigned kver_sub;
+    char unused;
+    ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub, &unused);
+    // Check the device kernel version
+    if (ret < 3) return 0;
+
+    return KVER(kver_major, kver_minor, kver_sub);
+}
+
+static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
+    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;                                                              \
+        }                                                                        \
+    } 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;                                                                  \
+        }                                                                            \
+    } 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;                                                               \
+        }                                                                         \
+    } 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;                                              \
+        }                                                        \
+    } while (0)
+
+}  // namespace bpf
+}  // namespace android
diff --git a/staticlibs/native/bpf_map_utils/include/bpf/WaitForProgsLoaded.h b/staticlibs/native/bpf_map_utils/include/bpf/WaitForProgsLoaded.h
new file mode 100644
index 0000000..bc4168e
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/include/bpf/WaitForProgsLoaded.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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 <log/log.h>
+
+#include <android-base/properties.h>
+
+namespace android {
+namespace bpf {
+
+// Wait for bpfloader to load BPF programs.
+static inline void waitForProgsLoaded() {
+    // infinite loop until success with 5/10/20/40/60/60/60... delay
+    for (int delay = 5;; delay *= 2) {
+        if (delay > 60) delay = 60;
+        if (android::base::WaitForProperty("bpf.progs_loaded", "1", std::chrono::seconds(delay)))
+            return;
+        ALOGW("Waited %ds for bpf.progs_loaded, still waiting...", delay);
+    }
+}
+
+}  // namespace bpf
+}  // namespace android