Merge "build multilib netd_integration_test binary" into pi-dev
diff --git a/bpfloader/BpfLoader.cpp b/bpfloader/BpfLoader.cpp
index 375ed92..8ab34d9 100644
--- a/bpfloader/BpfLoader.cpp
+++ b/bpfloader/BpfLoader.cpp
@@ -264,6 +264,7 @@
} // namespace bpf
} // namespace android
+using android::bpf::APP_UID_STATS_MAP_PATH;
using android::bpf::BPF_EGRESS_PROG_PATH;
using android::bpf::BPF_INGRESS_PROG_PATH;
using android::bpf::COOKIE_TAG_MAP_PATH;
@@ -276,6 +277,7 @@
using android::bpf::UID_STATS_MAP_PATH;
using android::bpf::XT_BPF_EGRESS_PROG_PATH;
using android::bpf::XT_BPF_INGRESS_PROG_PATH;
+
using android::bpf::ReplacePattern;
using android::bpf::loadAndAttachProgram;
@@ -291,6 +293,7 @@
int ret = 0;
DECLARE_MAP(cookieTagMap, COOKIE_TAG_MAP_PATH);
DECLARE_MAP(uidCounterSetMap, UID_COUNTERSET_MAP_PATH);
+ DECLARE_MAP(appUidStatsMap, APP_UID_STATS_MAP_PATH);
DECLARE_MAP(uidStatsMap, UID_STATS_MAP_PATH);
DECLARE_MAP(tagStatsMap, TAG_STATS_MAP_PATH);
DECLARE_MAP(ifaceStatsMap, IFACE_STATS_MAP_PATH);
@@ -301,6 +304,7 @@
const std::vector<ReplacePattern> mapPatterns = {
ReplacePattern(COOKIE_TAG_MAP, cookieTagMap.get()),
ReplacePattern(UID_COUNTERSET_MAP, uidCounterSetMap.get()),
+ ReplacePattern(APP_UID_STATS_MAP, appUidStatsMap.get()),
ReplacePattern(UID_STATS_MAP, uidStatsMap.get()),
ReplacePattern(TAG_STATS_MAP, tagStatsMap.get()),
ReplacePattern(IFACE_STATS_MAP, ifaceStatsMap.get()),
diff --git a/bpfloader/bpf_kern.h b/bpfloader/bpf_kern.h
index 69803e8..a59cb6d 100644
--- a/bpfloader/bpf_kern.h
+++ b/bpfloader/bpf_kern.h
@@ -14,6 +14,20 @@
* limitations under the License.
*/
+/*
+ * This h file together with bpf_kern.c is used for compiling the eBPF kernel
+ * program. To generate the bpf_kern.o file manually, use the clang prebuilt in
+ * this android tree to compile the files with --target=bpf options. For
+ * example, in system/netd/ directory, execute the following command:
+ * $: ANDROID_BASE_DIRECTORY/prebuilts/clang/host/linux-x86/clang-4691093/bin/clang \
+ * -I ANDROID_BASE_DIRECTORY/bionic/libc/kernel/uapi/ \
+ * -I ANDROID_BASE_DIRECTORY/system/netd/bpfloader/ \
+ * -I ANDROID_BASE_DIRECTORY/bionic/libc/kernel/android/uapi/ \
+ * -I ANDROID_BASE_DIRECTORY/bionic/libc/include \
+ * -I ANDROID_BASE_DIRECTORY/system/netd/libbpf/include \
+ * --target=bpf -O2 -c bpfloader/bpf_kern.c -o bpfloader/bpf_kern.o
+ */
+
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/in.h>
@@ -171,9 +185,8 @@
struct stats_key key = {.uid = uid, .tag = tag, .counterSet = 0, .ifaceIndex = skb->ifindex};
- uint32_t* counterSet;
- counterSet = find_map_entry(UID_COUNTERSET_MAP, &uid);
- if (counterSet) key.counterSet = *counterSet;
+ uint8_t* counterSet = find_map_entry(UID_COUNTERSET_MAP, &uid);
+ if (counterSet) key.counterSet = (uint32_t)*counterSet;
if (tag) {
bpf_update_stats(skb, TAG_STATS_MAP, direction, &key);
@@ -181,5 +194,6 @@
key.tag = 0;
bpf_update_stats(skb, UID_STATS_MAP, direction, &key);
+ bpf_update_stats(skb, APP_UID_STATS_MAP, direction, &uid);
return match;
}
diff --git a/libbpf/Android.bp b/libbpf/Android.bp
index cf467ce..cf16ae9 100644
--- a/libbpf/Android.bp
+++ b/libbpf/Android.bp
@@ -65,6 +65,7 @@
name: "libbpf_test",
srcs: [
"BpfNetworkStatsTest.cpp",
+ "BpfMapTest.cpp",
],
cflags: [
"-Wall",
diff --git a/libbpf/BpfMapTest.cpp b/libbpf/BpfMapTest.cpp
new file mode 100644
index 0000000..925117b
--- /dev/null
+++ b/libbpf/BpfMapTest.cpp
@@ -0,0 +1,290 @@
+/*
+ * 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 <netdutils/MockSyscalls.h>
+#include "bpf/BpfMap.h"
+#include "bpf/BpfNetworkStats.h"
+#include "bpf/BpfUtils.h"
+
+using ::testing::_;
+using ::testing::ByMove;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::StrictMock;
+using ::testing::Test;
+
+namespace android {
+namespace bpf {
+
+using base::unique_fd;
+using netdutils::StatusOr;
+
+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() {}
+ int mMapFd;
+
+ void SetUp() {
+ if (!access(PINNED_MAP_PATH, R_OK)) {
+ EXPECT_EQ(0, remove(PINNED_MAP_PATH));
+ }
+ mMapFd = createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint32_t), TEST_MAP_SIZE,
+ BPF_F_NO_PREALLOC);
+ }
+
+ void TearDown() {
+ if (!access(PINNED_MAP_PATH, R_OK)) {
+ EXPECT_EQ(0, remove(PINNED_MAP_PATH));
+ }
+ close(mMapFd);
+ }
+
+ void checkMapInvalid(BpfMap<uint32_t, uint32_t>& map) {
+ EXPECT_FALSE(map.isValid());
+ EXPECT_EQ(-1, map.getMap().get());
+ EXPECT_TRUE(map.getPinnedPath().empty());
+ }
+
+ 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_TRUE(isOk(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, StatusOr<uint32_t> value) {
+ ASSERT_TRUE(isOk(value.status()));
+ 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_TRUE(isOk(map.writeValue(key, value, BPF_ANY)));
+ }
+ }
+};
+
+TEST_F(BpfMapTest, constructor) {
+ BpfMap<uint32_t, uint32_t> testMap1;
+ checkMapInvalid(testMap1);
+
+ BpfMap<uint32_t, uint32_t> testMap2(mMapFd);
+ checkMapValid(testMap2);
+ EXPECT_TRUE(testMap2.getPinnedPath().empty());
+
+ BpfMap<uint32_t, uint32_t> testMap3(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ checkMapValid(testMap3);
+ EXPECT_TRUE(testMap3.getPinnedPath().empty());
+}
+
+TEST_F(BpfMapTest, basicHelpers) {
+ BpfMap<uint32_t, uint32_t> testMap(mMapFd);
+ uint32_t key = TEST_KEY1;
+ uint32_t value_write = TEST_VALUE1;
+ writeToMapAndCheck(testMap, key, value_write);
+ StatusOr<uint32_t> value_read = testMap.readValue(key);
+ checkValueAndStatus(value_write, value_read);
+ StatusOr<uint32_t> key_read = testMap.getFirstKey();
+ checkValueAndStatus(key, key_read);
+ ASSERT_TRUE(isOk(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;
+ testMap.reset(mMapFd);
+ uint32_t key = TEST_KEY1;
+ uint32_t value_write = TEST_VALUE1;
+ writeToMapAndCheck(testMap, key, value_write);
+ testMap.reset();
+ checkMapInvalid(testMap);
+ unique_fd invalidFd(mMapFd);
+ ASSERT_GT(0, findMapEntry(invalidFd, &key, &value_write));
+ ASSERT_EQ(EBADF, errno);
+}
+
+TEST_F(BpfMapTest, moveConstructor) {
+ BpfMap<uint32_t, uint32_t> testMap1(mMapFd);
+ 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, iterateEmptyMap) {
+ BpfMap<uint32_t, uint32_t> testMap(mMapFd);
+ auto itr = testMap.begin();
+ ASSERT_NE(testMap.end(), itr);
+ itr.start();
+ ASSERT_EQ(testMap.end(), itr);
+ ASSERT_FALSE(isOk(itr.next()));
+ ASSERT_EQ(testMap.end(), itr);
+}
+
+TEST_F(BpfMapTest, iterator) {
+ BpfMap<uint32_t, uint32_t> testMap(mMapFd);
+ for (uint32_t key = 0; key < TEST_MAP_SIZE; key++) {
+ uint32_t value = key * 10;
+ ASSERT_TRUE(isOk(testMap.writeValue(key, value, BPF_ANY)));
+ }
+ std::vector<uint32_t> valueList;
+ auto itr = testMap.begin();
+ for (itr.start(); itr != testMap.end(); itr.next()) {
+ uint32_t readKey = *itr;
+ StatusOr<uint32_t> readValue = testMap.readValue(readKey);
+ ASSERT_TRUE(isOk(readValue.status()));
+ valueList.push_back(readValue.value());
+ }
+ ASSERT_EQ((size_t)TEST_MAP_SIZE, valueList.size());
+ std::sort(valueList.begin(), valueList.end());
+ for (uint32_t key = 0; key < TEST_MAP_SIZE; key++) {
+ EXPECT_EQ(key * 10, valueList[key]);
+ }
+}
+
+TEST_F(BpfMapTest, twoIterator) {
+ BpfMap<uint32_t, uint32_t> testMap(mMapFd);
+ for (uint32_t key = 0; key < TEST_MAP_SIZE; key++) {
+ uint32_t value = key * 10;
+ ASSERT_TRUE(isOk(testMap.writeValue(key, value, BPF_ANY)));
+ }
+ auto itr1 = testMap.begin();
+ auto itr2 = testMap.begin();
+ ASSERT_EQ(itr1, itr2);
+ ASSERT_TRUE(isOk(itr1.start()));
+ ASSERT_NE(itr1, itr2);
+ ASSERT_TRUE(isOk(itr2.start()));
+ ASSERT_EQ(itr1, itr2);
+ uint32_t count = 0;
+ while (itr1 != testMap.end()) {
+ ASSERT_TRUE(isOk(itr1.next()));
+ count++;
+ }
+ ASSERT_EQ(testMap.end(), itr1);
+ ASSERT_EQ(TEST_MAP_SIZE, count);
+ while (count != 0) {
+ ASSERT_NE(testMap.end(), itr2);
+ count--;
+ ASSERT_TRUE(isOk(itr2.next()));
+ }
+ ASSERT_EQ(itr1, itr2);
+ ASSERT_EQ(testMap.end(), itr2);
+}
+
+TEST_F(BpfMapTest, pinnedToPath) {
+ BpfMap<uint32_t, uint32_t> testMap1(mMapFd);
+ EXPECT_TRUE(isOk(testMap1.pinToPath(PINNED_MAP_PATH)));
+ EXPECT_EQ(0, access(PINNED_MAP_PATH, R_OK));
+ EXPECT_EQ(0, testMap1.getPinnedPath().compare(PINNED_MAP_PATH));
+ BpfMap<uint32_t, uint32_t> testMap2(mapRetrieve(PINNED_MAP_PATH, 0));
+ checkMapValid(testMap2);
+ uint32_t key = TEST_KEY1;
+ uint32_t value = TEST_VALUE1;
+ writeToMapAndCheck(testMap1, key, value);
+ StatusOr<uint32_t> value_read = testMap2.readValue(key);
+ checkValueAndStatus(value, value_read);
+}
+
+TEST_F(BpfMapTest, SetUpMap) {
+ BpfMap<uint32_t, uint32_t> testMap1;
+ EXPECT_TRUE(isOk(testMap1.getOrCreate(TEST_MAP_SIZE, PINNED_MAP_PATH, BPF_MAP_TYPE_HASH)));
+ EXPECT_EQ(0, access(PINNED_MAP_PATH, R_OK));
+ checkMapValid(testMap1);
+ EXPECT_EQ(0, testMap1.getPinnedPath().compare(PINNED_MAP_PATH));
+ BpfMap<uint32_t, uint32_t> testMap2;
+ testMap2.getOrCreate(TEST_MAP_SIZE, PINNED_MAP_PATH, BPF_MAP_TYPE_HASH);
+ checkMapValid(testMap2);
+ EXPECT_EQ(0, testMap2.getPinnedPath().compare(PINNED_MAP_PATH));
+ uint32_t key = TEST_KEY1;
+ uint32_t value = TEST_VALUE1;
+ writeToMapAndCheck(testMap1, key, value);
+ StatusOr<uint32_t> value_read = testMap2.readValue(key);
+ checkValueAndStatus(value, value_read);
+}
+
+TEST_F(BpfMapTest, iterate) {
+ BpfMap<uint32_t, uint32_t> testMap(mMapFd);
+ 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_TRUE(isOk(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);
+ EXPECT_FALSE(isOk(testMap.getFirstKey()));
+}
+
+TEST_F(BpfMapTest, iterateWithValue) {
+ BpfMap<uint32_t, uint32_t> testMap(mMapFd);
+ 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_TRUE(isOk(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);
+ EXPECT_FALSE(isOk(testMap.getFirstKey()));
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/libbpf/BpfNetworkStats.cpp b/libbpf/BpfNetworkStats.cpp
index acde1e6..eafbb5e 100644
--- a/libbpf/BpfNetworkStats.cpp
+++ b/libbpf/BpfNetworkStats.cpp
@@ -25,8 +25,8 @@
#include "android-base/file.h"
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
+#include "bpf/BpfMap.h"
#include "bpf/BpfNetworkStats.h"
-#include "bpf/BpfUtils.h"
#ifdef LOG_TAG
#undef LOG_TAG
@@ -37,81 +37,72 @@
namespace android {
namespace bpf {
-
-// The limit for stats received by a unknown interface;
-static const int64_t MAX_UNKNOWN_IFACE_BYTES = 100*1000;
+using netdutils::Status;
static constexpr uint32_t BPF_OPEN_FLAGS = BPF_F_RDONLY;
-int bpfGetUidStatsInternal(uid_t uid, Stats* stats, const base::unique_fd& map_fd) {
- struct StatsKey nonExistentKey = NONEXISTENT_STATSKEY;
- struct StatsValue dummyValue;
- auto processUidStats = [uid, stats](void *key, const base::unique_fd& map_fd) {
- if (((StatsKey *) key)->uid != uid) {
- return BPF_CONTINUE;
- }
- StatsValue statsEntry;
- int ret = bpf::findMapEntry(map_fd, key, &statsEntry);
- if (ret) return -errno;
- stats->rxPackets += statsEntry.rxPackets;
- stats->txPackets += statsEntry.txPackets;
- stats->rxBytes += statsEntry.rxBytes;
- stats->txBytes += statsEntry.txBytes;
- return BPF_CONTINUE;
- };
- return bpfIterateMap(nonExistentKey, dummyValue, map_fd, processUidStats);
+int bpfGetUidStatsInternal(uid_t uid, Stats* stats,
+ const BpfMap<uint32_t, StatsValue>& appUidStatsMap) {
+ auto statsEntry = appUidStatsMap.readValue(uid);
+ if (isOk(statsEntry)) {
+ stats->rxPackets = statsEntry.value().rxPackets;
+ stats->txPackets = statsEntry.value().txPackets;
+ stats->rxBytes = statsEntry.value().rxBytes;
+ stats->txBytes = statsEntry.value().txBytes;
+ }
+ return -statsEntry.status().code();
}
int bpfGetUidStats(uid_t uid, Stats* stats) {
- base::unique_fd uidStatsMap(bpf::mapRetrieve(UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
- if (uidStatsMap < 0) {
+ BpfMap<uint32_t, StatsValue> appUidStatsMap(
+ mapRetrieve(APP_UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+
+ if (!appUidStatsMap.isValid()) {
int ret = -errno;
- ALOGE("Opening map fd from %s failed: %s", UID_STATS_MAP_PATH, strerror(errno));
+ ALOGE("Opening appUidStatsMap(%s) failed: %s", UID_STATS_MAP_PATH, strerror(errno));
return ret;
}
- return bpfGetUidStatsInternal(uid, stats, uidStatsMap);
+ return bpfGetUidStatsInternal(uid, stats, appUidStatsMap);
}
int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
- const base::unique_fd& ifaceStatsMapFd,
- const base::unique_fd& ifaceNameMapFd) {
- uint32_t nonExistentKey = NONEXISTENT_IFACE_STATS_KEY;
- struct StatsValue dummyValue;
+ const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
+ const BpfMap<uint32_t, IfaceValue>& ifaceNameMap) {
int64_t unknownIfaceBytesTotal = 0;
stats->tcpRxPackets = -1;
stats->tcpTxPackets = -1;
- auto processIfaceStats = [iface, stats, &ifaceNameMapFd, &unknownIfaceBytesTotal](
- void* key, const base::unique_fd& ifaceStatsMapFd) {
+ const auto processIfaceStats = [iface, stats, &ifaceNameMap, &unknownIfaceBytesTotal]
+ (const uint32_t& key,
+ const BpfMap<uint32_t, StatsValue>& ifaceStatsMap) {
char ifname[IFNAMSIZ];
- int ifIndex = *(int *)key;
- if (getIfaceNameFromMap(ifaceNameMapFd, ifaceStatsMapFd, ifIndex, ifname, &ifIndex,
+ if (getIfaceNameFromMap(ifaceNameMap, ifaceStatsMap, key, ifname, key,
&unknownIfaceBytesTotal)) {
- return BPF_CONTINUE;
+ return netdutils::status::ok;
}
if (!iface || !strcmp(iface, ifname)) {
StatsValue statsEntry;
- int ret = bpf::findMapEntry(ifaceStatsMapFd, &ifIndex, &statsEntry);
- if (ret) return -errno;
+ ASSIGN_OR_RETURN(statsEntry, ifaceStatsMap.readValue(key));
stats->rxPackets += statsEntry.rxPackets;
stats->txPackets += statsEntry.txPackets;
stats->rxBytes += statsEntry.rxBytes;
stats->txBytes += statsEntry.txBytes;
}
- return BPF_CONTINUE;
+ return netdutils::status::ok;
};
- return bpfIterateMap(nonExistentKey, dummyValue, ifaceStatsMapFd, processIfaceStats);
+ return -ifaceStatsMap.iterate(processIfaceStats).code();
}
int bpfGetIfaceStats(const char* iface, Stats* stats) {
- base::unique_fd ifaceStatsMap(bpf::mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+ BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
int ret;
- if (ifaceStatsMap < 0) {
+ if (!ifaceStatsMap.isValid()) {
ret = -errno;
ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
return ret;
}
- base::unique_fd ifaceIndexNameMap(bpf::mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
- if (ifaceIndexNameMap < 0) {
+ BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
+ mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
+ if (!ifaceIndexNameMap.isValid()) {
ret = -errno;
ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
return ret;
@@ -133,80 +124,53 @@
return newLine;
}
-void maybeLogUnknownIface(int ifaceIndex, const base::unique_fd& statsMapFd, void* curKey,
- int64_t* unknownIfaceBytesTotal) {
- // Have we already logged an error?
- if (*unknownIfaceBytesTotal == -1) {
- return;
- }
-
- // Are we undercounting enough data to be worth logging?
- StatsValue statsEntry;
- if (bpf::findMapEntry(statsMapFd, curKey, &statsEntry) < 0) {
- // No data is being undercounted.
- return;
- }
-
- *unknownIfaceBytesTotal += (statsEntry.rxBytes + statsEntry.txBytes);
- if (*unknownIfaceBytesTotal >= MAX_UNKNOWN_IFACE_BYTES) {
- ALOGE("Unknown name for ifindex %d with more than %" PRId64 " bytes of traffic",
- ifaceIndex, *unknownIfaceBytesTotal);
- *unknownIfaceBytesTotal = -1;
- }
-}
-
-int getIfaceNameFromMap(const base::unique_fd& ifaceMapFd, const base::unique_fd& statsMapFd,
- uint32_t ifaceIndex, char* ifname, void* curKey,
- int64_t* unknownIfaceBytesTotal) {
- if (bpf::findMapEntry(ifaceMapFd, &ifaceIndex, ifname) < 0) {
- maybeLogUnknownIface(ifaceIndex, statsMapFd, curKey, unknownIfaceBytesTotal);
- return -ENODEV;
- }
- return 0;
-}
-
int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
const std::vector<std::string>& limitIfaces, int limitTag,
- int limitUid, const base::unique_fd& statsMapFd,
- const base::unique_fd& ifaceMapFd) {
+ int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
+ const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
int64_t unknownIfaceBytesTotal = 0;
- struct StatsKey nonExistentKey = NONEXISTENT_STATSKEY;
- struct StatsValue dummyValue;
- auto processDetailUidStats = [lines, &limitIfaces, limitTag, limitUid,
- &unknownIfaceBytesTotal, &ifaceMapFd]
- (void* key, const base::unique_fd& statsMapFd) {
- struct StatsKey curKey = * (struct StatsKey*)key;
+ const auto processDetailUidStats = [lines, &limitIfaces, &limitTag, &limitUid,
+ &unknownIfaceBytesTotal,
+ &ifaceMap](const StatsKey& key,
+ const BpfMap<StatsKey, StatsValue>& statsMap) {
char ifname[IFNAMSIZ];
- if (getIfaceNameFromMap(ifaceMapFd, statsMapFd, curKey.ifaceIndex, ifname, &curKey,
+ if (getIfaceNameFromMap(ifaceMap, statsMap, key.ifaceIndex, ifname, key,
&unknownIfaceBytesTotal)) {
- return BPF_CONTINUE;
+ return netdutils::status::ok;
}
std::string ifnameStr(ifname);
if (limitIfaces.size() > 0 &&
std::find(limitIfaces.begin(), limitIfaces.end(), ifnameStr) == limitIfaces.end()) {
// Nothing matched; skip this line.
- return BPF_CONTINUE;
+ return netdutils::status::ok;
}
- if (limitTag != TAG_ALL && uint32_t(limitTag) != curKey.tag) {
- return BPF_CONTINUE;
+ if (limitTag != TAG_ALL && uint32_t(limitTag) != key.tag) {
+ return netdutils::status::ok;
}
- if (limitUid != UID_ALL && uint32_t(limitUid) != curKey.uid) {
- return BPF_CONTINUE;
+ if (limitUid != UID_ALL && uint32_t(limitUid) != key.uid) {
+ return netdutils::status::ok;
}
StatsValue statsEntry;
- if (bpf::findMapEntry(statsMapFd, &curKey, &statsEntry) < 0) return -errno;
- lines->push_back(populateStatsEntry(curKey, statsEntry, ifname));
- return BPF_CONTINUE;
+ ASSIGN_OR_RETURN(statsEntry, statsMap.readValue(key));
+ lines->push_back(populateStatsEntry(key, statsEntry, ifname));
+ return netdutils::status::ok;
};
- return bpfIterateMap(nonExistentKey, dummyValue, statsMapFd, processDetailUidStats);
+ Status res = statsMap.iterate(processDetailUidStats);
+ if (!isOk(res)) {
+ ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
+ strerror(res.code()));
+ return -res.code();
+ }
+ return 0;
}
int parseBpfNetworkStatsDetail(std::vector<stats_line>* lines,
const std::vector<std::string>& limitIfaces, int limitTag,
int limitUid) {
int ret = 0;
- base::unique_fd ifaceIndexNameMap(bpf::mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
- if (ifaceIndexNameMap < 0) {
+ BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
+ mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
+ if (!ifaceIndexNameMap.isValid()) {
ret = -errno;
ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
return ret;
@@ -214,8 +178,8 @@
// If the caller did not pass in TAG_NONE, read tag data.
if (limitTag != TAG_NONE) {
- base::unique_fd tagStatsMap(bpf::mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
- if (tagStatsMap < 0) {
+ BpfMap<StatsKey, StatsValue> tagStatsMap(mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+ if (!tagStatsMap.isValid()) {
ret = -errno;
ALOGE("get tagStats map fd failed: %s", strerror(errno));
return ret;
@@ -228,8 +192,8 @@
// If the caller did not pass in a specific tag (i.e., if limitTag is TAG_NONE(0) or
// TAG_ALL(-1)) read UID data.
if (limitTag == TAG_NONE || limitTag == TAG_ALL) {
- base::unique_fd uidStatsMap(bpf::mapRetrieve(UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
- if (uidStatsMap < 0) {
+ BpfMap<StatsKey, StatsValue> uidStatsMap(mapRetrieve(UID_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+ if (!uidStatsMap.isValid()) {
ret = -errno;
ALOGE("Opening map fd from %s failed: %s", UID_STATS_MAP_PATH, strerror(errno));
return ret;
@@ -241,39 +205,42 @@
}
int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
- const base::unique_fd& statsMapFd,
- const base::unique_fd& ifaceMapFd) {
+ const BpfMap<uint32_t, StatsValue>& statsMap,
+ const BpfMap<uint32_t, IfaceValue>& ifaceMap) {
int64_t unknownIfaceBytesTotal = 0;
- uint32_t nonExistentKey = NONEXISTENT_IFACE_STATS_KEY;
- struct StatsValue dummyValue;
- auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMapFd](
- void* key, void* value, const base::unique_fd& statsMapFd) {
- uint32_t ifIndex = *(uint32_t*)key;
+ const auto processDetailIfaceStats = [lines, &unknownIfaceBytesTotal, &ifaceMap, &statsMap](
+ const uint32_t& key, const StatsValue& value,
+ const BpfMap<uint32_t, StatsValue>&) {
char ifname[IFNAMSIZ];
- if (getIfaceNameFromMap(ifaceMapFd, statsMapFd, ifIndex, ifname, &ifIndex,
- &unknownIfaceBytesTotal)) {
- return BPF_CONTINUE;
+ if (getIfaceNameFromMap(ifaceMap, statsMap, key, ifname, key, &unknownIfaceBytesTotal)) {
+ return netdutils::status::ok;
}
- StatsValue* statsEntry = (StatsValue*)value;
StatsKey fakeKey = {
.uid = (uint32_t)UID_ALL, .counterSet = (uint32_t)SET_ALL, .tag = (uint32_t)TAG_NONE};
- lines->push_back(populateStatsEntry(fakeKey, *statsEntry, ifname));
- return BPF_CONTINUE;
+ lines->push_back(populateStatsEntry(fakeKey, value, ifname));
+ return netdutils::status::ok;
};
- return bpfIterateMapWithValue(nonExistentKey, dummyValue, statsMapFd, processDetailIfaceStats);
+ Status res = statsMap.iterateWithValue(processDetailIfaceStats);
+ if (!isOk(res)) {
+ ALOGE("failed to iterate per uid Stats map for detail traffic stats: %s",
+ strerror(res.code()));
+ return -res.code();
+ }
+ return 0;
}
int parseBpfNetworkStatsDev(std::vector<stats_line>* lines) {
int ret = 0;
- base::unique_fd ifaceIndexNameMap(bpf::mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
- if (ifaceIndexNameMap < 0) {
+ BpfMap<uint32_t, IfaceValue> ifaceIndexNameMap(
+ mapRetrieve(IFACE_INDEX_NAME_MAP_PATH, BPF_OPEN_FLAGS));
+ if (!ifaceIndexNameMap.isValid()) {
ret = -errno;
ALOGE("get ifaceIndexName map fd failed: %s", strerror(errno));
return ret;
}
- base::unique_fd ifaceStatsMap(bpf::mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
- if (ifaceStatsMap < 0) {
+ BpfMap<uint32_t, StatsValue> ifaceStatsMap(mapRetrieve(IFACE_STATS_MAP_PATH, BPF_OPEN_FLAGS));
+ if (!ifaceStatsMap.isValid()) {
ret = -errno;
ALOGE("get ifaceStats map fd failed: %s", strerror(errno));
return ret;
@@ -285,76 +252,5 @@
return (uint64_t)uid << 32 | tag;
}
-// This function get called when the system_server decided to clean up the
-// tagStatsMap after it gethered the information of taggged socket stats. The
-// function go through all the entry in tagStatsMap and remove all the entry
-// for which the tag no longer exists.
-int cleanStatsMapInternal(const base::unique_fd& cookieTagMap, const base::unique_fd& tagStatsMap) {
- uint64_t curCookie = 0;
- uint64_t nextCookie = 0;
- int res;
- UidTag tmp_uidtag;
- std::unordered_set<uint64_t> uidTagSet;
- StatsKey curKey, nextKey;
-
- // Find all the uid, tag pair exist in cookieTagMap.
- while (bpf::getNextMapKey(cookieTagMap, &curCookie, &nextCookie) != -1) {
- curCookie = nextCookie;
- res = bpf::findMapEntry(cookieTagMap, &curCookie, &tmp_uidtag);
- if (res < 0) {
- // might be a concurrent delete, continue to check other entries.
- continue;
- }
- uint64_t uidTag = combineUidTag(tmp_uidtag.uid, tmp_uidtag.tag);
- uidTagSet.insert(uidTag);
- }
-
- // Find all the entries in tagStatsMap where the key is not in the set of
- // uid, tag pairs found above.
- curKey = NONEXISTENT_STATSKEY;
- std::vector<StatsKey> keyList;
- while (bpf::getNextMapKey(tagStatsMap, &curKey, &nextKey) != -1) {
- curKey = nextKey;
- uint64_t uidTag = combineUidTag(curKey.uid, curKey.tag);
- if (uidTagSet.find(uidTag) == uidTagSet.end()) {
- keyList.push_back(curKey);
- }
- }
-
- // Delete the entries
- int size = keyList.size();
- while (!keyList.empty()) {
- StatsKey key = keyList.back();
- keyList.pop_back();
- res = bpf::deleteMapEntry(tagStatsMap, &key);
- if (res < 0 && errno != ENOENT) {
- res = -errno;
- ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", key.uid, key.tag, strerror(errno));
- return res;
- }
- }
- ALOGD("finish clean up, %d stats entry cleaned", size);
- return 0;
-}
-
-int cleanStatsMap() {
- base::unique_fd cookieTagMap(bpf::mapRetrieve(COOKIE_TAG_MAP_PATH, BPF_OPEN_FLAGS));
- int ret = 0;
- if (cookieTagMap < 0) {
- ret = -errno;
- ALOGE("get cookieTag map fd failed: %s", strerror(errno));
- return ret;
- }
-
- base::unique_fd tagStatsMap(bpf::mapRetrieve(TAG_STATS_MAP_PATH, BPF_OPEN_FLAGS));
- if (tagStatsMap < 0) {
- ret = -errno;
- ALOGE("get tagStats map fd failed: %s", strerror(errno));
- return ret;
- }
-
- return cleanStatsMapInternal(cookieTagMap, tagStatsMap);
-}
-
} // namespace bpf
} // namespace android
diff --git a/libbpf/BpfNetworkStatsTest.cpp b/libbpf/BpfNetworkStatsTest.cpp
index d1f8157..8fd4943 100644
--- a/libbpf/BpfNetworkStatsTest.cpp
+++ b/libbpf/BpfNetworkStatsTest.cpp
@@ -34,6 +34,7 @@
#include <android-base/strings.h>
#include <netdutils/MockSyscalls.h>
+#include "bpf/BpfMap.h"
#include "bpf/BpfNetworkStats.h"
#include "bpf/BpfUtils.h"
@@ -50,7 +51,6 @@
namespace bpf {
using base::unique_fd;
-using netdutils::status::ok;
constexpr int TEST_MAP_SIZE = 10;
constexpr uid_t TEST_UID1 = 10086;
@@ -58,76 +58,78 @@
constexpr uint32_t TEST_TAG = 42;
constexpr int TEST_COUNTERSET0 = 0;
constexpr int TEST_COUNTERSET1 = 1;
-constexpr const int COUNTERSETS_LIMIT = 2;
constexpr uint64_t TEST_BYTES0 = 1000;
constexpr uint64_t TEST_BYTES1 = 2000;
constexpr uint64_t TEST_PACKET0 = 100;
constexpr uint64_t TEST_PACKET1 = 200;
-constexpr const char* IFACE_NAME1 = "lo";
-constexpr const char* IFACE_NAME2 = "wlan0";
-constexpr const char* IFACE_NAME3 = "rmnet_data0";
+constexpr const char IFACE_NAME1[] = "lo";
+constexpr const char IFACE_NAME2[] = "wlan0";
+constexpr const char IFACE_NAME3[] = "rmnet_data0";
+// A iface name that the size is bigger then IFNAMSIZ
+constexpr const char LONG_IFACE_NAME[] = "wlanWithALongName";
+constexpr const char TRUNCATED_IFACE_NAME[] = "wlanWithALongNa";
constexpr uint32_t IFACE_INDEX1 = 1;
constexpr uint32_t IFACE_INDEX2 = 2;
constexpr uint32_t IFACE_INDEX3 = 3;
+constexpr uint32_t IFACE_INDEX4 = 4;
constexpr uint32_t UNKNOWN_IFACE = 0;
class BpfNetworkStatsHelperTest : public testing::Test {
protected:
BpfNetworkStatsHelperTest() {}
- unique_fd mFakeCookieTagMap;
- unique_fd mFakeUidStatsMap;
- unique_fd mFakeTagStatsMap;
- unique_fd mFakeIfaceIndexNameMap;
- unique_fd mFakeIfaceStatsMap;
+ BpfMap<uint64_t, UidTag> mFakeCookieTagMap;
+ BpfMap<uint32_t, StatsValue> mFakeAppUidStatsMap;
+ BpfMap<StatsKey, StatsValue> mFakeUidStatsMap;
+ BpfMap<StatsKey, StatsValue> mFakeTagStatsMap;
+ BpfMap<uint32_t, IfaceValue> mFakeIfaceIndexNameMap;
+ BpfMap<uint32_t, StatsValue> mFakeIfaceStatsMap;
void SetUp() {
- mFakeCookieTagMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(uint64_t),
- sizeof(struct UidTag), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeCookieTagMap);
+ mFakeCookieTagMap = BpfMap<uint64_t, UidTag>(createMap(
+ BPF_MAP_TYPE_HASH, sizeof(uint64_t), sizeof(struct UidTag), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeCookieTagMap.getMap());
- mFakeUidStatsMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
- sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeUidStatsMap);
+ mFakeAppUidStatsMap = BpfMap<uint32_t, StatsValue>(createMap(
+ BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeAppUidStatsMap.getMap());
- mFakeTagStatsMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
- sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeTagStatsMap);
+ mFakeUidStatsMap =
+ BpfMap<StatsKey, StatsValue>(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
+ sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeUidStatsMap.getMap());
- mFakeIfaceIndexNameMap =
- unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), IFNAMSIZ, TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeIfaceIndexNameMap);
+ mFakeTagStatsMap =
+ BpfMap<StatsKey, StatsValue>(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
+ sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeTagStatsMap.getMap());
- mFakeIfaceStatsMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t),
- sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeIfaceStatsMap);
- }
+ mFakeIfaceIndexNameMap = BpfMap<uint32_t, IfaceValue>(
+ createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(IfaceValue), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeIfaceIndexNameMap.getMap());
- void TearDown() {
- mFakeCookieTagMap.reset();
- mFakeUidStatsMap.reset();
- mFakeTagStatsMap.reset();
- mFakeIfaceIndexNameMap.reset();
- mFakeIfaceStatsMap.reset();
+ mFakeIfaceStatsMap = BpfMap<uint32_t, StatsValue>(createMap(
+ BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeIfaceStatsMap.getMap());
}
void expectUidTag(uint64_t cookie, uid_t uid, uint32_t tag) {
- struct UidTag tagResult;
- EXPECT_EQ(0, findMapEntry(mFakeCookieTagMap, &cookie, &tagResult));
- EXPECT_EQ(uid, tagResult.uid);
- EXPECT_EQ(tag, tagResult.tag);
+ auto tagResult = mFakeCookieTagMap.readValue(cookie);
+ EXPECT_TRUE(isOk(tagResult));
+ EXPECT_EQ(uid, tagResult.value().uid);
+ EXPECT_EQ(tag, tagResult.value().tag);
}
void populateFakeStats(uid_t uid, uint32_t tag, uint32_t ifaceIndex, uint32_t counterSet,
- StatsValue* value, const base::unique_fd& map_fd) {
+ StatsValue value, BpfMap<StatsKey, StatsValue>& map) {
StatsKey key = {
.uid = (uint32_t)uid, .tag = tag, .counterSet = counterSet, .ifaceIndex = ifaceIndex};
- EXPECT_EQ(0, writeToMapEntry(map_fd, &key, value, BPF_ANY));
+ EXPECT_TRUE(isOk(map.writeValue(key, value, BPF_ANY)));
}
void updateIfaceMap(const char* ifaceName, uint32_t ifaceIndex) {
- char iface[IFNAMSIZ];
- strlcpy(iface, ifaceName, IFNAMSIZ);
- EXPECT_EQ(0, writeToMapEntry(mFakeIfaceIndexNameMap, &ifaceIndex, iface, BPF_ANY));
+ IfaceValue iface;
+ strlcpy(iface.name, ifaceName, IFNAMSIZ);
+ EXPECT_TRUE(isOk(mFakeIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY)));
}
void expectStatsEqual(const StatsValue& target, const Stats& result) {
@@ -158,55 +160,83 @@
for (int i = 0; i < 5; i++) {
uint64_t cookie = i + 1;
struct UidTag tag = {.uid = TEST_UID1, .tag = TEST_TAG};
- EXPECT_EQ(0, writeToMapEntry(mFakeCookieTagMap, &cookie, &tag, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeCookieTagMap.writeValue(cookie, tag, BPF_ANY)));
}
uint64_t curCookie = 0;
- uint64_t nextCookie = 0;
- struct UidTag tagResult;
- EXPECT_EQ(0, getNextMapKey(mFakeCookieTagMap, &curCookie, &nextCookie));
- uint64_t headOfMap = nextCookie;
- curCookie = nextCookie;
+ auto nextCookie = mFakeCookieTagMap.getNextKey(curCookie);
+ EXPECT_TRUE(isOk(nextCookie));
+ uint64_t headOfMap = nextCookie.value();
+ curCookie = nextCookie.value();
// Find the second entry in the map, then immediately delete it.
- EXPECT_EQ(0, getNextMapKey(mFakeCookieTagMap, &curCookie, &nextCookie));
- EXPECT_EQ(0, deleteMapEntry(mFakeCookieTagMap, &nextCookie));
+ nextCookie = mFakeCookieTagMap.getNextKey(curCookie);
+ EXPECT_TRUE(isOk(nextCookie));
+ EXPECT_TRUE(isOk(mFakeCookieTagMap.deleteValue((nextCookie.value()))));
// Find the entry that is now immediately after headOfMap, then delete that.
- EXPECT_EQ(0, getNextMapKey(mFakeCookieTagMap, &curCookie, &nextCookie));
- EXPECT_EQ(0, deleteMapEntry(mFakeCookieTagMap, &nextCookie));
+ nextCookie = mFakeCookieTagMap.getNextKey(curCookie);
+ EXPECT_TRUE(isOk(nextCookie));
+ EXPECT_TRUE(isOk(mFakeCookieTagMap.deleteValue((nextCookie.value()))));
// Attempting to read an entry that has been deleted fails with ENOENT.
- curCookie = nextCookie;
- EXPECT_EQ(-1, findMapEntry(mFakeCookieTagMap, &curCookie, &tagResult));
- EXPECT_EQ(ENOENT, errno);
+ curCookie = nextCookie.value();
+ auto tagResult = mFakeCookieTagMap.readValue(curCookie);
+ EXPECT_EQ(ENOENT, tagResult.status().code());
// Finding the entry after our deleted entry restarts iteration from the beginning of the map.
- EXPECT_EQ(0, getNextMapKey(mFakeCookieTagMap, &curCookie, &nextCookie));
- EXPECT_EQ(headOfMap, nextCookie);
+ nextCookie = mFakeCookieTagMap.getNextKey(curCookie);
+ EXPECT_TRUE(isOk(nextCookie));
+ EXPECT_EQ(headOfMap, nextCookie.value());
+}
+
+TEST_F(BpfNetworkStatsHelperTest, TestBpfIterateMap) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ for (int i = 0; i < 5; i++) {
+ uint64_t cookie = i + 1;
+ struct UidTag tag = {.uid = TEST_UID1, .tag = TEST_TAG};
+ EXPECT_TRUE(isOk(mFakeCookieTagMap.writeValue(cookie, tag, BPF_ANY)));
+ }
+ int totalCount = 0;
+ int totalSum = 0;
+ const auto iterateWithoutDeletion = [&totalCount, &totalSum](const uint64_t& key,
+ const BpfMap<uint64_t, UidTag>&) {
+ EXPECT_GE((uint64_t)5, key);
+ totalCount++;
+ totalSum += key;
+ return netdutils::status::ok;
+ };
+ EXPECT_TRUE(isOk(mFakeCookieTagMap.iterate(iterateWithoutDeletion)));
+ EXPECT_EQ(5, totalCount);
+ EXPECT_EQ(1 + 2 + 3 + 4 + 5, totalSum);
}
TEST_F(BpfNetworkStatsHelperTest, TestGetUidStatsTotal) {
SKIP_IF_BPF_NOT_SUPPORTED;
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
+ updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
+ updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
StatsValue value1 = {.rxBytes = TEST_BYTES0,
.rxPackets = TEST_PACKET0,
.txBytes = TEST_BYTES1,
.txPackets = TEST_PACKET1,};
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
- populateFakeStats(TEST_UID2, 0, IFACE_INDEX1, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
- Stats result1 = {};
- ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeUidStatsMap));
- StatsValue uid1Value = {
+ StatsValue value2 = {
.rxBytes = TEST_BYTES0 * 2,
.rxPackets = TEST_PACKET0 * 2,
.txBytes = TEST_BYTES1 * 2,
.txPackets = TEST_PACKET1 * 2,
};
- expectStatsEqual(uid1Value, result1);
+ ASSERT_TRUE(isOk(mFakeAppUidStatsMap.writeValue(TEST_UID1, value1, BPF_ANY)));
+ ASSERT_TRUE(isOk(mFakeAppUidStatsMap.writeValue(TEST_UID2, value2, BPF_ANY)));
+ Stats result1 = {};
+ ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID1, &result1, mFakeAppUidStatsMap));
+ expectStatsEqual(value1, result1);
Stats result2 = {};
- ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID2, &result2, mFakeUidStatsMap));
- expectStatsEqual(value1, result2);
+ ASSERT_EQ(0, bpfGetUidStatsInternal(TEST_UID2, &result2, mFakeAppUidStatsMap));
+ expectStatsEqual(value2, result2);
std::vector<stats_line> lines;
std::vector<std::string> ifaces;
+ populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET1, value1, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID2, 0, IFACE_INDEX3, TEST_COUNTERSET1, value1, mFakeUidStatsMap);
ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, TEST_UID1,
mFakeUidStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)2, lines.size());
@@ -214,7 +244,7 @@
ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, TEST_UID2,
mFakeUidStatsMap, mFakeIfaceIndexNameMap));
ASSERT_EQ((unsigned long)1, lines.size());
- expectStatsLineEqual(value1, IFACE_NAME1, TEST_UID2, TEST_COUNTERSET1, 0, lines.front());
+ expectStatsLineEqual(value1, IFACE_NAME3, TEST_UID2, TEST_COUNTERSET1, 0, lines.front());
}
TEST_F(BpfNetworkStatsHelperTest, TestGetIfaceStatsInternal) {
@@ -236,11 +266,11 @@
.txPackets = TEST_PACKET0,
};
uint32_t ifaceStatsKey = IFACE_INDEX1;
- EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value1, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
ifaceStatsKey = IFACE_INDEX2;
- EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value2, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY)));
ifaceStatsKey = IFACE_INDEX3;
- EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value1, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
Stats result1 = {};
ASSERT_EQ(0, bpfGetIfaceStatsInternal(IFACE_NAME1, &result1, mFakeIfaceStatsMap,
@@ -271,14 +301,11 @@
.rxPackets = TEST_PACKET0,
.txBytes = TEST_BYTES1,
.txPackets = TEST_PACKET1,};
- populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, &value1,
+ populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeTagStatsMap);
+ populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX2, TEST_COUNTERSET0, value1, mFakeTagStatsMap);
+ populateFakeStats(TEST_UID1, TEST_TAG + 1, IFACE_INDEX1, TEST_COUNTERSET0, value1,
mFakeTagStatsMap);
- populateFakeStats(TEST_UID1, TEST_TAG, IFACE_INDEX2, TEST_COUNTERSET0, &value1,
- mFakeTagStatsMap);
- populateFakeStats(TEST_UID1, TEST_TAG + 1, IFACE_INDEX1, TEST_COUNTERSET0, &value1,
- mFakeTagStatsMap);
- populateFakeStats(TEST_UID2, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, &value1,
- mFakeTagStatsMap);
+ populateFakeStats(TEST_UID2, TEST_TAG, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeTagStatsMap);
std::vector<stats_line> lines;
std::vector<std::string> ifaces;
ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL,
@@ -309,11 +336,11 @@
.rxPackets = TEST_PACKET0,
.txBytes = TEST_BYTES1,
.txPackets = TEST_PACKET1,};
- populateFakeStats(0, 0, 0, COUNTERSETS_LIMIT, &value1, mFakeUidStatsMap);
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET1, &value1, mFakeUidStatsMap);
- populateFakeStats(TEST_UID2, 0, IFACE_INDEX1, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
+ populateFakeStats(0, 0, 0, OVERFLOW_COUNTERSET, value1, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET0, value1, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET1, value1, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID2, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeUidStatsMap);
std::vector<stats_line> lines;
std::vector<std::string> ifaces;
ASSERT_EQ(0, parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL,
@@ -335,25 +362,6 @@
ASSERT_EQ((unsigned long)2, lines.size());
}
-TEST_F(BpfNetworkStatsHelperTest, TestGetStatsWithNoExistKey) {
- SKIP_IF_BPF_NOT_SUPPORTED;
-
- updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
- StatsValue value1 = {
- .rxBytes = TEST_BYTES0,
- .rxPackets = TEST_PACKET0,
- .txBytes = TEST_BYTES1,
- .txPackets = TEST_PACKET1,
- };
- populateFakeStats(DEFAULT_OVERFLOWUID, 0, 0, 0, &value1, mFakeUidStatsMap);
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
- std::vector<stats_line> lines;
- std::vector<std::string> ifaces;
- ASSERT_EQ(-EUCLEAN,
- parseBpfNetworkStatsDetailInternal(&lines, ifaces, TAG_ALL, UID_ALL, mFakeUidStatsMap,
- mFakeIfaceIndexNameMap));
-}
-
TEST_F(BpfNetworkStatsHelperTest, TestUnkownIfaceError) {
SKIP_IF_BPF_NOT_SUPPORTED;
@@ -363,13 +371,13 @@
.txBytes = TEST_BYTES1 * 20,
.txPackets = TEST_PACKET1,};
uint32_t ifaceIndex = UNKNOWN_IFACE;
- populateFakeStats(TEST_UID1, 0, ifaceIndex, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, &value1, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID1, 0, ifaceIndex, TEST_COUNTERSET0, value1, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID1, 0, IFACE_INDEX1, TEST_COUNTERSET0, value1, mFakeUidStatsMap);
StatsValue value2 = {.rxBytes = TEST_BYTES0 * 40,
.rxPackets = TEST_PACKET0,
.txBytes = TEST_BYTES1 * 40,
.txPackets = TEST_PACKET1,};
- populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET0, &value2, mFakeUidStatsMap);
+ populateFakeStats(TEST_UID1, 0, IFACE_INDEX2, TEST_COUNTERSET0, value2, mFakeUidStatsMap);
StatsKey curKey = {.uid = TEST_UID1,
.tag = 0,
.ifaceIndex = ifaceIndex,
@@ -377,11 +385,11 @@
char ifname[IFNAMSIZ];
int64_t unknownIfaceBytesTotal = 0;
ASSERT_EQ(-ENODEV, getIfaceNameFromMap(mFakeIfaceIndexNameMap, mFakeUidStatsMap, ifaceIndex,
- ifname, &curKey, &unknownIfaceBytesTotal));
+ ifname, curKey, &unknownIfaceBytesTotal));
ASSERT_EQ(((int64_t)(TEST_BYTES0 * 20 + TEST_BYTES1 * 20)), unknownIfaceBytesTotal);
curKey.ifaceIndex = IFACE_INDEX2;
ASSERT_EQ(-ENODEV, getIfaceNameFromMap(mFakeIfaceIndexNameMap, mFakeUidStatsMap, ifaceIndex,
- ifname, &curKey, &unknownIfaceBytesTotal));
+ ifname, curKey, &unknownIfaceBytesTotal));
ASSERT_EQ(-1, unknownIfaceBytesTotal);
std::vector<stats_line> lines;
std::vector<std::string> ifaces;
@@ -398,6 +406,7 @@
updateIfaceMap(IFACE_NAME1, IFACE_INDEX1);
updateIfaceMap(IFACE_NAME2, IFACE_INDEX2);
updateIfaceMap(IFACE_NAME3, IFACE_INDEX3);
+ updateIfaceMap(LONG_IFACE_NAME, IFACE_INDEX4);
StatsValue value1 = {
.rxBytes = TEST_BYTES0,
.rxPackets = TEST_PACKET0,
@@ -411,21 +420,25 @@
.txPackets = TEST_PACKET0,
};
uint32_t ifaceStatsKey = IFACE_INDEX1;
- EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value1, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
ifaceStatsKey = IFACE_INDEX2;
- EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value2, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY)));
ifaceStatsKey = IFACE_INDEX3;
- EXPECT_EQ(0, writeToMapEntry(mFakeIfaceStatsMap, &ifaceStatsKey, &value1, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value1, BPF_ANY)));
+ ifaceStatsKey = IFACE_INDEX4;
+ EXPECT_TRUE(isOk(mFakeIfaceStatsMap.writeValue(ifaceStatsKey, value2, BPF_ANY)));
std::vector<stats_line> lines;
ASSERT_EQ(0,
parseBpfNetworkStatsDevInternal(&lines, mFakeIfaceStatsMap, mFakeIfaceIndexNameMap));
- ASSERT_EQ((unsigned long)3, lines.size());
+ ASSERT_EQ((unsigned long)4, lines.size());
std::sort(lines.begin(), lines.end(), [](const auto& line1, const auto& line2)-> bool {
return strcmp(line1.iface, line2.iface) < 0;
});
expectStatsLineEqual(value1, IFACE_NAME1, UID_ALL, SET_ALL, TAG_NONE, lines[0]);
expectStatsLineEqual(value1, IFACE_NAME3, UID_ALL, SET_ALL, TAG_NONE, lines[1]);
expectStatsLineEqual(value2, IFACE_NAME2, UID_ALL, SET_ALL, TAG_NONE, lines[2]);
+ ASSERT_EQ(0, strcmp(TRUNCATED_IFACE_NAME, lines[3].iface));
+ expectStatsLineEqual(value2, TRUNCATED_IFACE_NAME, UID_ALL, SET_ALL, TAG_NONE, lines[3]);
}
} // namespace bpf
} // namespace android
diff --git a/libbpf/BpfUtils.cpp b/libbpf/BpfUtils.cpp
index d6ea6d7..9a587fb 100644
--- a/libbpf/BpfUtils.cpp
+++ b/libbpf/BpfUtils.cpp
@@ -40,9 +40,31 @@
using android::netdutils::statusFromErrno;
using android::netdutils::StatusOr;
+#define ptr_to_u64(x) ((uint64_t)(uintptr_t)x)
+#define DEFAULT_LOG_LEVEL 1
+
namespace android {
namespace bpf {
+/* The bpf_attr is a union which might have a much larger size then the struct we are using, while
+ * The inline initializer only reset the field we are using and leave the reset of the memory as
+ * is. The bpf kernel code will performs a much stricter check to ensure all unused field is 0. So
+ * this syscall will normally fail with E2BIG if we don't do a memset to bpf_attr.
+ */
+bool operator==(const StatsKey& lhs, const StatsKey& rhs) {
+ return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag) && (lhs.counterSet == rhs.counterSet) &&
+ (lhs.ifaceIndex == rhs.ifaceIndex));
+}
+
+bool operator==(const UidTag& lhs, const UidTag& rhs) {
+ return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag));
+}
+
+bool operator==(const StatsValue& lhs, const StatsValue& rhs) {
+ return ((lhs.rxBytes == rhs.rxBytes) && (lhs.txBytes == rhs.txBytes) &&
+ (lhs.rxPackets == rhs.rxPackets) && (lhs.txPackets == rhs.txPackets));
+}
+
int bpf(int cmd, Slice bpfAttr) {
return syscall(__NR_bpf, cmd, bpfAttr.base(), bpfAttr.size());
}
@@ -100,6 +122,16 @@
return bpf(BPF_MAP_GET_NEXT_KEY, Slice(&attr, sizeof(attr)));
}
+int getFirstMapKey(const base::unique_fd& map_fd, void* firstKey) {
+ bpf_attr attr;
+ memset(&attr, 0, sizeof(attr));
+ attr.map_fd = map_fd.get();
+ attr.key = 0;
+ attr.next_key = ptr_to_u64(firstKey);
+
+ return bpf(BPF_MAP_GET_NEXT_KEY, Slice(&attr, sizeof(attr)));
+}
+
int bpfProgLoad(bpf_prog_type prog_type, Slice bpf_insns, const char* license,
uint32_t kern_version, Slice bpf_log) {
bpf_attr attr;
@@ -174,36 +206,6 @@
return sock_cookie;
}
-StatusOr<unique_fd> setUpBPFMap(uint32_t key_size, uint32_t value_size, uint32_t map_size,
- const char* path, bpf_map_type map_type) {
- int ret;
- base::unique_fd map_fd;
- ret = access(path, R_OK);
- /* Check the pinned location first to check if the map is already there.
- * otherwise create a new one.
- */
- if (ret == 0) {
- map_fd = base::unique_fd(mapRetrieve(path, 0));
- if (map_fd < 0) {
- return statusFromErrno(errno, StringPrintf("pinned map not accessible or does not "
- "exist: (%s)\n",
- path));
- }
- } else if (ret < 0 && errno == ENOENT) {
- map_fd = base::unique_fd(createMap(map_type, key_size, value_size, map_size, BPF_F_NO_PREALLOC));
- if (map_fd < 0) {
- return statusFromErrno(errno, StringPrintf("map create failed!: %s", path));
- }
- ret = mapPin(map_fd, path);
- if (ret) {
- return statusFromErrno(errno, StringPrintf("bpf map pin(%d, %s)", map_fd.get(), path));
- }
- } else {
- return statusFromErrno(errno, StringPrintf("pinned map not accessible: %s", path));
- }
- return map_fd;
-}
-
bool hasBpfSupport() {
struct utsname buf;
int kernel_version_major;
diff --git a/libbpf/include/bpf/BpfMap.h b/libbpf/include/bpf/BpfMap.h
new file mode 100644
index 0000000..81d7efe
--- /dev/null
+++ b/libbpf/include/bpf/BpfMap.h
@@ -0,0 +1,327 @@
+/*
+ * 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.
+ */
+
+#ifndef BPF_BPFMAP_H
+#define BPF_BPFMAP_H
+
+#include <linux/bpf.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <utils/Log.h>
+#include "bpf/BpfUtils.h"
+#include "netdutils/Status.h"
+#include "netdutils/StatusOr.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 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:
+ class const_iterator {
+ public:
+ netdutils::Status start() {
+ if (mMap == nullptr) {
+ return netdutils::statusFromErrno(EINVAL, "Invalid map iterator");
+ }
+ auto firstKey = mMap->getFirstKey();
+ if (isOk(firstKey)) {
+ mCurKey = firstKey.value();
+ } else if (firstKey.status().code() == ENOENT) {
+ // The map is empty.
+ mMap = nullptr;
+ memset(&mCurKey, 0, sizeof(Key));
+ } else {
+ return firstKey.status();
+ }
+ return netdutils::status::ok;
+ }
+
+ netdutils::StatusOr<Key> next() {
+ if (mMap == nullptr) {
+ return netdutils::statusFromErrno(ENOENT, "Iterating past end of map");
+ }
+ auto nextKey = mMap->getNextKey(mCurKey);
+ if (isOk(nextKey)) {
+ mCurKey = nextKey.value();
+ } else if (nextKey.status().code() == ENOENT) {
+ // iterator reached the end of map
+ mMap = nullptr;
+ memset(&mCurKey, 0, sizeof(Key));
+ } else {
+ return nextKey.status();
+ }
+ return mCurKey;
+ }
+
+ const Key operator*() { return mCurKey; }
+
+ bool operator==(const const_iterator& other) const {
+ return (mMap == other.mMap) && (mCurKey == other.mCurKey);
+ }
+
+ bool operator!=(const const_iterator& other) const { return !(*this == other); }
+
+ const_iterator(const BpfMap<Key, Value>* map) : mMap(map) {
+ memset(&mCurKey, 0, sizeof(Key));
+ }
+
+ private:
+ const BpfMap<Key, Value> * mMap;
+ Key mCurKey;
+ };
+
+ BpfMap<Key, Value>() : mMapFd(-1){};
+ BpfMap<Key, Value>(int fd) : mMapFd(fd){};
+ BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags) {
+ int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags);
+ if (map_fd < 0) {
+ mMapFd.reset(-1);
+ } else {
+ mMapFd.reset(map_fd);
+ }
+ }
+
+ netdutils::Status pinToPath(const std::string path) {
+ int ret = mapPin(mMapFd, path.c_str());
+ if (ret) {
+ return netdutils::statusFromErrno(errno,
+ base::StringPrintf("pin to %s failed", path.c_str()));
+ }
+ mPinnedPath = path;
+ return netdutils::status::ok;
+ }
+
+ netdutils::StatusOr<Key> getFirstKey() const {
+ Key firstKey;
+ if (getFirstMapKey(mMapFd, &firstKey)) {
+ return netdutils::statusFromErrno(
+ errno, base::StringPrintf("Get firstKey map %d failed", mMapFd.get()));
+ }
+ return firstKey;
+ }
+
+ netdutils::StatusOr<Key> getNextKey(const Key& key) const {
+ Key nextKey;
+ if (getNextMapKey(mMapFd, const_cast<Key*>(&key), &nextKey)) {
+ return netdutils::statusFromErrno(
+ errno, base::StringPrintf("Get next key of map %d failed", mMapFd.get()));
+ }
+ return nextKey;
+ }
+
+ netdutils::Status writeValue(const Key& key, const Value& value, uint64_t flags) {
+ if (writeToMapEntry(mMapFd, const_cast<Key*>(&key), const_cast<Value*>(&value), flags)) {
+ return netdutils::statusFromErrno(
+ errno, base::StringPrintf("write to map %d failed", mMapFd.get()));
+ }
+ return netdutils::status::ok;
+ }
+
+ netdutils::StatusOr<Value> readValue(const Key key) const {
+ Value value;
+ if (findMapEntry(mMapFd, const_cast<Key*>(&key), &value)) {
+ return netdutils::statusFromErrno(
+ errno, base::StringPrintf("read value of map %d failed", mMapFd.get()));
+ }
+ return value;
+ }
+
+ netdutils::Status deleteValue(const Key& key) {
+ if (deleteMapEntry(mMapFd, const_cast<Key*>(&key))) {
+ return netdutils::statusFromErrno(
+ errno, base::StringPrintf("delete entry from map %d failed", mMapFd.get()));
+ }
+ return netdutils::status::ok;
+ }
+
+ // Function that tries to get map from a pinned path, if the map doesn't
+ // exist yet, create a new one and pinned to the path.
+ netdutils::Status getOrCreate(const uint32_t maxEntries, const char* path,
+ const bpf_map_type mapType);
+
+ // Iterate through the map and handle each key retrieved based on the filter
+ // without modification of map content.
+ netdutils::Status iterate(
+ const std::function<netdutils::Status(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.
+ netdutils::Status iterateWithValue(
+ const std::function<netdutils::Status(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
+ netdutils::Status iterate(
+ const std::function<netdutils::Status(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.
+ netdutils::Status iterateWithValue(
+ const std::function<netdutils::Status(const Key& key, const Value& value,
+ BpfMap<Key, Value>& map)>& filter);
+
+ const base::unique_fd& getMap() const { return mMapFd; };
+
+ const std::string getPinnedPath() const { return mPinnedPath; };
+
+ // Move constructor
+ void operator=(BpfMap<Key, Value>&& other) {
+ mMapFd = std::move(other.mMapFd);
+ if (!other.mPinnedPath.empty()) {
+ mPinnedPath = other.mPinnedPath;
+ } else {
+ mPinnedPath.clear();
+ }
+ other.reset();
+ }
+
+ void reset(int fd = -1) {
+ mMapFd.reset(fd);
+ mPinnedPath.clear();
+ }
+
+ bool isValid() const { return mMapFd != -1; }
+
+ const_iterator begin() const { return const_iterator(this); }
+
+ const_iterator end() const { return const_iterator(nullptr); }
+
+ private:
+ base::unique_fd mMapFd;
+ std::string mPinnedPath;
+};
+
+template <class Key, class Value>
+netdutils::Status BpfMap<Key, Value>::getOrCreate(const uint32_t maxEntries, const char* path,
+ bpf_map_type mapType) {
+ int ret = access(path, R_OK);
+ /* Check the pinned location first to check if the map is already there.
+ * otherwise create a new one.
+ */
+ if (ret == 0) {
+ mMapFd = base::unique_fd(mapRetrieve(path, 0));
+ if (mMapFd == -1) {
+ reset();
+ return netdutils::statusFromErrno(
+ errno,
+ base::StringPrintf("pinned map not accessible or does not exist: (%s)\n", path));
+ }
+ mPinnedPath = path;
+ } else if (ret == -1 && errno == ENOENT) {
+ mMapFd = base::unique_fd(
+ createMap(mapType, sizeof(Key), sizeof(Value), maxEntries, BPF_F_NO_PREALLOC));
+ if (mMapFd == -1) {
+ reset();
+ return netdutils::statusFromErrno(errno,
+ base::StringPrintf("map create failed!: %s", path));
+ }
+ netdutils::Status pinStatus = pinToPath(path);
+ if (!isOk(pinStatus)) {
+ reset();
+ return pinStatus;
+ }
+ mPinnedPath = path;
+ } else {
+ return netdutils::statusFromErrno(
+ errno, base::StringPrintf("pinned map not accessible: %s", path));
+ }
+ return netdutils::status::ok;
+}
+
+template <class Key, class Value>
+netdutils::Status BpfMap<Key, Value>::iterate(
+ const std::function<netdutils::Status(const Key& key, const BpfMap<Key, Value>& map)>& filter)
+ const {
+ const_iterator itr = this->begin();
+ RETURN_IF_NOT_OK(itr.start());
+ while (itr != this->end()) {
+ Key prevKey = *itr;
+ netdutils::Status advanceStatus = itr.next();
+ RETURN_IF_NOT_OK(filter(prevKey, *this));
+ RETURN_IF_NOT_OK(advanceStatus);
+ }
+ return netdutils::status::ok;
+}
+
+template <class Key, class Value>
+netdutils::Status BpfMap<Key, Value>::iterateWithValue(
+ const std::function<netdutils::Status(const Key& key, const Value& value,
+ const BpfMap<Key, Value>& map)>& filter) const {
+ const_iterator itr = this->begin();
+ RETURN_IF_NOT_OK(itr.start());
+ while (itr != this->end()) {
+ Key prevKey = *itr;
+ Value prevValue;
+ ASSIGN_OR_RETURN(prevValue, this->readValue(prevKey));
+ netdutils::Status advanceStatus = itr.next();
+ RETURN_IF_NOT_OK(filter(prevKey, prevValue, *this));
+ RETURN_IF_NOT_OK(advanceStatus);
+ }
+ return netdutils::status::ok;
+}
+
+template <class Key, class Value>
+netdutils::Status BpfMap<Key, Value>::iterate(
+ const std::function<netdutils::Status(const Key& key, BpfMap<Key, Value>& map)>& filter) {
+ const_iterator itr = this->begin();
+ RETURN_IF_NOT_OK(itr.start());
+ while (itr != this->end()) {
+ Key prevKey = *itr;
+ netdutils::Status advanceStatus = itr.next();
+ RETURN_IF_NOT_OK(filter(prevKey, *this));
+ RETURN_IF_NOT_OK(advanceStatus);
+ }
+ return netdutils::status::ok;
+}
+
+template <class Key, class Value>
+netdutils::Status BpfMap<Key, Value>::iterateWithValue(
+ const std::function<netdutils::Status(const Key& key, const Value& value,
+ BpfMap<Key, Value>& map)>& filter) {
+ const_iterator itr = this->begin();
+ RETURN_IF_NOT_OK(itr.start());
+ while (itr != this->end()) {
+ Key prevKey = *itr;
+ Value prevValue;
+ ASSIGN_OR_RETURN(prevValue, this->readValue(prevKey));
+ netdutils::Status advanceStatus = itr.next();
+ RETURN_IF_NOT_OK(filter(prevKey, prevValue, *this));
+ RETURN_IF_NOT_OK(advanceStatus);
+ }
+ return netdutils::status::ok;
+}
+
+} // namespace bpf
+} // namespace android
+
+#endif
diff --git a/libbpf/include/bpf/BpfNetworkStats.h b/libbpf/include/bpf/BpfNetworkStats.h
index 63cf0bc..80dff88 100644
--- a/libbpf/include/bpf/BpfNetworkStats.h
+++ b/libbpf/include/bpf/BpfNetworkStats.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <bpf/BpfMap.h>
+
namespace android {
namespace bpf {
@@ -26,6 +28,12 @@
constexpr int SET_DEFAULT = 0;
constexpr int SET_FOREGROUND = 1;
+// The limit for stats received by a unknown interface;
+constexpr const int64_t MAX_UNKNOWN_IFACE_BYTES = 100 * 1000;
+
+// This is a JNI ABI and is used by
+// framework/base/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+// make sure it is consistent with the JNI code before changing this.
struct stats_line {
char iface[32];
int32_t uid;
@@ -37,26 +45,60 @@
int64_t txPackets;
};
// For test only
-int bpfGetUidStatsInternal(uid_t uid, struct Stats* stats, const base::unique_fd& map_fd);
+int bpfGetUidStatsInternal(uid_t uid, struct Stats* stats,
+ const BpfMap<uint32_t, StatsValue>& appUidStatsMap);
// For test only
int bpfGetIfaceStatsInternal(const char* iface, Stats* stats,
- const base::unique_fd& ifaceStatsMapFd,
- const base::unique_fd& ifaceNameMapFd);
+ const BpfMap<uint32_t, StatsValue>& ifaceStatsMap,
+ const BpfMap<uint32_t, IfaceValue>& ifaceNameMap);
// For test only
int parseBpfNetworkStatsDetailInternal(std::vector<stats_line>* lines,
const std::vector<std::string>& limitIfaces, int limitTag,
- int limitUid, const base::unique_fd& statsMapFd,
- const base::unique_fd& ifaceMapFd);
+ int limitUid, const BpfMap<StatsKey, StatsValue>& statsMap,
+ const BpfMap<uint32_t, IfaceValue>& ifaceMap);
// For test only
int cleanStatsMapInternal(const base::unique_fd& cookieTagMap, const base::unique_fd& tagStatsMap);
// For test only
-int getIfaceNameFromMap(const base::unique_fd& ifaceMapFd, const base::unique_fd& statsMapFd,
- uint32_t ifaceIndex, char* ifname, void* curKey,
- int64_t* unknownIfaceBytesTotal);
+template <class Key>
+int getIfaceNameFromMap(const BpfMap<uint32_t, IfaceValue>& ifaceMap,
+ const BpfMap<Key, StatsValue>& statsMap, uint32_t ifaceIndex, char* ifname,
+ const Key& curKey, int64_t* unknownIfaceBytesTotal) {
+ auto iface = ifaceMap.readValue(ifaceIndex);
+ if (!isOk(iface)) {
+ maybeLogUnknownIface(ifaceIndex, statsMap, curKey, unknownIfaceBytesTotal);
+ return -ENODEV;
+ }
+ strlcpy(ifname, iface.value().name, sizeof(IfaceValue));
+ return 0;
+}
+
+template <class Key>
+void maybeLogUnknownIface(int ifaceIndex, const BpfMap<Key, StatsValue>& statsMap,
+ const Key& curKey, int64_t* unknownIfaceBytesTotal) {
+ // Have we already logged an error?
+ if (*unknownIfaceBytesTotal == -1) {
+ return;
+ }
+
+ // Are we undercounting enough data to be worth logging?
+ auto statsEntry = statsMap.readValue(curKey);
+ if (!netdutils::isOk(statsEntry)) {
+ // No data is being undercounted.
+ return;
+ }
+
+ *unknownIfaceBytesTotal += (statsEntry.value().rxBytes + statsEntry.value().txBytes);
+ if (*unknownIfaceBytesTotal >= MAX_UNKNOWN_IFACE_BYTES) {
+ ALOGE("Unknown name for ifindex %d with more than %" PRId64 " bytes of traffic", ifaceIndex,
+ *unknownIfaceBytesTotal);
+ *unknownIfaceBytesTotal = -1;
+ }
+}
+
// For test only
int parseBpfNetworkStatsDevInternal(std::vector<stats_line>* lines,
- const base::unique_fd& statsMapFd,
- const base::unique_fd& ifaceMapFd);
+ const BpfMap<uint32_t, StatsValue>& statsMap,
+ const BpfMap<uint32_t, IfaceValue>& ifaceMap);
int bpfGetUidStats(uid_t uid, struct Stats* stats);
int bpfGetIfaceStats(const char* iface, struct Stats* stats);
diff --git a/libbpf/include/bpf/BpfUtils.h b/libbpf/include/bpf/BpfUtils.h
index 550c9ca..ac107df 100644
--- a/libbpf/include/bpf/BpfUtils.h
+++ b/libbpf/include/bpf/BpfUtils.h
@@ -14,10 +14,14 @@
* limitations under the License.
*/
+#ifndef BPF_BPFUTILS_H
+#define BPF_BPFUTILS_H
+
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/unistd.h>
+#include <net/if.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
@@ -26,9 +30,6 @@
#include "netdutils/Slice.h"
#include "netdutils/StatusOr.h"
-#define ptr_to_u64(x) ((uint64_t)(uintptr_t)x)
-#define DEFAULT_LOG_LEVEL 1
-
#define BPF_PASS 1
#define BPF_DROP 0
@@ -63,12 +64,48 @@
uint64_t tcpTxPackets;
};
+struct IfaceValue {
+ char name[IFNAMSIZ];
+};
+
#ifndef DEFAULT_OVERFLOWUID
#define DEFAULT_OVERFLOWUID 65534
#endif
#define BPF_PATH "/sys/fs/bpf"
+// Since we cannot garbage collect the stats map since device boot, we need to make these maps as
+// large as possible. The maximum size of number of map entries we can have is depend on the rlimit
+// of MEM_LOCK granted to netd. The memory space needed by each map can be calculated by the
+// following fomula:
+// elem_size = 40 + roundup(key_size, 8) + roundup(value_size, 8)
+// cost = roundup_pow_of_two(max_entries) * 16 + elem_size * max_entries +
+// elem_size * number_of_CPU
+// And the cost of each map currently used is(assume the device have 8 CPUs):
+// cookie_tag_map: key: 8 bytes, value: 8 bytes, cost: 822592 bytes = 823Kbytes
+// uid_counter_set_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// app_uid_stats_map: key: 4 bytes, value: 32 bytes, cost: 1062784 bytes = 1063Kbytes
+// uid_stats_map: key: 16 bytes, value: 32 bytes, cost: 1142848 bytes = 1143Kbytes
+// tag_stats_map: key: 16 bytes, value: 32 bytes, cost: 1142848 bytes = 1143Kbytes
+// iface_index_name_map:key: 4 bytes, value: 16 bytes, cost: 80896 bytes = 81Kbytes
+// iface_stats_map: key: 4 bytes, value: 32 bytes, cost: 97024 bytes = 97Kbytes
+// dozable_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// standby_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// powersave_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// total: 4930Kbytes
+// It takes maximum 4.9MB kernel memory space if all maps are full, which requires any devices
+// running this module to have a memlock rlimit to be larger then 5MB. In the old qtaguid module,
+// we don't have a total limit for data entries but only have limitation of tags each uid can have.
+// (default is 1024 in kernel);
+
+constexpr const int COOKIE_UID_MAP_SIZE = 10000;
+constexpr const int UID_COUNTERSET_MAP_SIZE = 2000;
+constexpr const int UID_STATS_MAP_SIZE = 10000;
+constexpr const int TAG_STATS_MAP_SIZE = 10000;
+constexpr const int IFACE_INDEX_NAME_MAP_SIZE = 1000;
+constexpr const int IFACE_STATS_MAP_SIZE = 1000;
+constexpr const int UID_OWNER_MAP_SIZE = 2000;
+
constexpr const char* BPF_EGRESS_PROG_PATH = BPF_PATH "/egress_prog";
constexpr const char* BPF_INGRESS_PROG_PATH = BPF_PATH "/ingress_prog";
constexpr const char* XT_BPF_INGRESS_PROG_PATH = BPF_PATH "/xt_bpf_ingress_prog";
@@ -78,6 +115,7 @@
constexpr const char* COOKIE_TAG_MAP_PATH = BPF_PATH "/traffic_cookie_tag_map";
constexpr const char* UID_COUNTERSET_MAP_PATH = BPF_PATH "/traffic_uid_counterSet_map";
+constexpr const char* APP_UID_STATS_MAP_PATH = BPF_PATH "/traffic_app_uid_stats_map";
constexpr const char* UID_STATS_MAP_PATH = BPF_PATH "/traffic_uid_stats_map";
constexpr const char* TAG_STATS_MAP_PATH = BPF_PATH "/traffic_tag_stats_map";
constexpr const char* IFACE_INDEX_NAME_MAP_PATH = BPF_PATH "/traffic_iface_index_name_map";
@@ -86,13 +124,9 @@
constexpr const char* STANDBY_UID_MAP_PATH = BPF_PATH "/traffic_standby_uid_map";
constexpr const char* POWERSAVE_UID_MAP_PATH = BPF_PATH "/traffic_powersave_uid_map";
-const StatsKey NONEXISTENT_STATSKEY = {
- .uid = DEFAULT_OVERFLOWUID,
-};
+constexpr const int OVERFLOW_COUNTERSET = 2;
constexpr const uint64_t NONEXISTENT_COOKIE = 0;
-constexpr const uint32_t NONEXISTENT_UID = DEFAULT_OVERFLOWUID;
-constexpr const uint32_t NONEXISTENT_IFACE_STATS_KEY = 0;
constexpr const int MINIMUM_API_REQUIRED = 28;
@@ -102,6 +136,7 @@
int findMapEntry(const base::unique_fd& map_fd, void* key, void* value);
int deleteMapEntry(const base::unique_fd& map_fd, void* key);
int getNextMapKey(const base::unique_fd& map_fd, void* key, void* next_key);
+int getFirstMapKey(const base::unique_fd& map_fd, void* firstKey);
int bpfProgLoad(bpf_prog_type prog_type, netdutils::Slice bpf_insns, const char* license,
uint32_t kern_version, netdutils::Slice bpf_log);
int mapPin(const base::unique_fd& map_fd, const char* pathname);
@@ -109,94 +144,20 @@
int attachProgram(bpf_attach_type type, uint32_t prog_fd, uint32_t cg_fd);
int detachProgram(bpf_attach_type type, uint32_t cg_fd);
uint64_t getSocketCookie(int sockFd);
-netdutils::StatusOr<base::unique_fd> setUpBPFMap(uint32_t key_size, uint32_t value_size,
- uint32_t map_size, const char* path,
- bpf_map_type map_type);
bool hasBpfSupport();
#define SKIP_IF_BPF_NOT_SUPPORTED \
do { \
if (!hasBpfSupport()) return; \
- } while (0);
+ } while (0)
constexpr int BPF_CONTINUE = 0;
constexpr int BPF_DELETED = 1;
-typedef std::function<int(void* key, const base::unique_fd& map_fd)> BpfMapEntryFilter;
-template <class Key, class Value>
-int bpfIterateMap(const Key& nonExistentKey, const Value& /* dummy */,
- const base::unique_fd& map_fd, const BpfMapEntryFilter& filter) {
- Key curKey = nonExistentKey;
- Key nextKey;
- int ret;
- Value dummyEntry;
- if (bpf::findMapEntry(map_fd, &curKey, &dummyEntry) == 0) {
- ALOGE("This entry should never exist in map!");
- return -EUCLEAN;
- }
- while (bpf::getNextMapKey(map_fd, &curKey, &nextKey) != -1) {
- ret = filter(&nextKey, map_fd);
- switch (ret) {
- case BPF_DELETED:
- // The filter deleted the entry. Find the next key by looking up the same key
- // we looked up this time.
- continue;
- case BPF_CONTINUE:
- curKey = nextKey;
- continue;
- default:
- return ret;
- }
- }
- // Return errno if getNextMapKey return error before hit to the end of the map.
- if (errno != ENOENT) {
- ret = errno;
- ALOGE("bpfIterateMap failed on MAP_FD: %d, error: %s", map_fd.get(), strerror(errno));
- return -ret;
- }
- return 0;
-}
-
-typedef std::function<int(void* key, void* value, const base::unique_fd& map_fd)>
- BpfMapEntryFilterWithValue;
-template <class Key, class Value>
-int bpfIterateMapWithValue(const Key& nonExistentKey, const Value& /* dummy */,
- const base::unique_fd& map_fd,
- const BpfMapEntryFilterWithValue& filter) {
- Key curKey = nonExistentKey;
- Key nextKey;
- int ret = 0;
- Value value;
- if (bpf::findMapEntry(map_fd, &curKey, &value) == 0) {
- ALOGE("This entry should never exist in map!");
- return -EUCLEAN;
- }
- while (bpf::getNextMapKey(map_fd, &curKey, &nextKey) != -1) {
- ret = bpf::findMapEntry(map_fd, &nextKey, &value);
- if (ret) {
- ALOGE("Get value failed");
- return ret;
- }
- ret = filter(&nextKey, &value, map_fd);
- switch (ret) {
- case BPF_DELETED:
- // The filter deleted the entry. Find the next key by looking up the same key
- // we looked up this time.
- continue;
- case BPF_CONTINUE:
- curKey = nextKey;
- continue;
- default:
- return ret;
- }
- }
- // Return errno if getNextMapKey return error before hit to the end of the map.
- if (errno != ENOENT) {
- ret = errno;
- ALOGE("bpfIterateMap failed on MAP_FD: %d, error: %s", map_fd.get(), strerror(errno));
- return -ret;
- }
- return 0;
-}
+bool operator==(const StatsValue& lhs, const StatsValue& rhs);
+bool operator==(const UidTag& lhs, const UidTag& rhs);
+bool operator==(const StatsKey& lhs, const StatsKey& rhs);
} // namespace bpf
} // namespace android
+
+#endif
diff --git a/libbpf/include/bpf/bpf_shared.h b/libbpf/include/bpf/bpf_shared.h
index 4a4904d..217b76f 100644
--- a/libbpf/include/bpf/bpf_shared.h
+++ b/libbpf/include/bpf/bpf_shared.h
@@ -23,6 +23,7 @@
#define COOKIE_TAG_MAP 0xbfceaaffffffffff
#define UID_COUNTERSET_MAP 0xbfdceeafffffffff
+#define APP_UID_STATS_MAP 0xbfa1daafffffffff
#define UID_STATS_MAP 0xbfdaafffffffffff
#define TAG_STATS_MAP 0xbfaaafffffffffff
#define IFACE_STATS_MAP 0xbf1faceaafffffff
diff --git a/server/TrafficController.cpp b/server/TrafficController.cpp
index 17c8f9a..d6a6480 100644
--- a/server/TrafficController.cpp
+++ b/server/TrafficController.cpp
@@ -44,7 +44,7 @@
#include <netdutils/Misc.h>
#include <netdutils/Syscalls.h>
#include "TrafficController.h"
-#include "bpf/BpfUtils.h"
+#include "bpf/BpfMap.h"
#include "bpf/bpf_shared.h"
#include "DumpWriter.h"
@@ -116,73 +116,75 @@
ebpfSupported = hasBpfSupport();
}
-Status initialOwnerMap(const unique_fd& map_fd, const std::string mapName) {
+Status initialOwnerMap(BpfMap<uint32_t, uint8_t>& map) {
+ // Check and delete all the entries from the map in case it is a runtime
+ // restart
+ const auto deleteAllEntries = [](const uint32_t& key, BpfMap<uint32_t, uint8_t>& map) {
+ Status res = map.deleteValue(key);
+ if (!isOk(res) && (res.code() == ENOENT)) {
+ ALOGE("Failed to delete data(uid=%u): %s\n", key, strerror(res.code()));
+ }
+ return netdutils::status::ok;
+ };
+ // It is safe to delete from this map because nothing will concurrently iterate over it:
+ // - Nothing in netd will iterate over it because we're holding mOwnerMatchMutex.
+ // - Nothing outside netd iterates over it.
+ map.iterate(deleteAllEntries);
uint32_t mapSettingKey = UID_MAP_ENABLED;
- uint32_t defaultMapState = 0;
- int ret = writeToMapEntry(map_fd, &mapSettingKey, &defaultMapState, BPF_NOEXIST);
- // If it is already exist, it might be a runtime restart and we just keep
- // the old state.
- if (ret && errno != EEXIST) {
- return statusFromErrno(errno, "Fail to set the initial state of " + mapName);
- }
- return netdutils::status::ok;
+ uint8_t defaultMapState = 0;
+ return map.writeValue(mapSettingKey, defaultMapState, BPF_NOEXIST);
}
Status TrafficController::initMaps() {
std::lock_guard<std::mutex> ownerMapGuard(mOwnerMatchMutex);
- std::lock_guard<std::mutex> statsMapGuard(mDeleteStatsMutex);
- ASSIGN_OR_RETURN(mCookieTagMap,
- setUpBPFMap(sizeof(uint64_t), sizeof(struct UidTag), COOKIE_UID_MAP_SIZE,
- COOKIE_TAG_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(
+ mCookieTagMap.getOrCreate(COOKIE_UID_MAP_SIZE, COOKIE_TAG_MAP_PATH, BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(COOKIE_TAG_MAP_PATH, AID_NET_BW_ACCT, "CookieTagMap",
false));
- ASSIGN_OR_RETURN(mUidCounterSetMap,
- setUpBPFMap(sizeof(uint32_t), sizeof(uint32_t), UID_COUNTERSET_MAP_SIZE,
- UID_COUNTERSET_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(mUidCounterSetMap.getOrCreate(UID_COUNTERSET_MAP_SIZE, UID_COUNTERSET_MAP_PATH,
+ BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(UID_COUNTERSET_MAP_PATH, AID_NET_BW_ACCT,
"UidCounterSetMap", false));
- ASSIGN_OR_RETURN(mUidStatsMap,
- setUpBPFMap(sizeof(struct StatsKey), sizeof(struct StatsValue),
- UID_STATS_MAP_SIZE, UID_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(
+ mAppUidStatsMap.getOrCreate(UID_STATS_MAP_SIZE, APP_UID_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(
+ changeOwnerAndMode(APP_UID_STATS_MAP_PATH, AID_NET_BW_STATS, "AppUidStatsMap", false));
+
+ RETURN_IF_NOT_OK(
+ mUidStatsMap.getOrCreate(UID_STATS_MAP_SIZE, UID_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(UID_STATS_MAP_PATH, AID_NET_BW_STATS, "UidStatsMap",
false));
- ASSIGN_OR_RETURN(mTagStatsMap,
- setUpBPFMap(sizeof(struct StatsKey), sizeof(struct StatsValue),
- TAG_STATS_MAP_SIZE, TAG_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(
+ mTagStatsMap.getOrCreate(TAG_STATS_MAP_SIZE, TAG_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(TAG_STATS_MAP_PATH, AID_NET_BW_STATS, "TagStatsMap",
false));
- ASSIGN_OR_RETURN(mIfaceIndexNameMap,
- setUpBPFMap(sizeof(uint32_t), IFNAMSIZ, IFACE_INDEX_NAME_MAP_SIZE,
- IFACE_INDEX_NAME_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(mIfaceIndexNameMap.getOrCreate(IFACE_INDEX_NAME_MAP_SIZE,
+ IFACE_INDEX_NAME_MAP_PATH, BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(IFACE_INDEX_NAME_MAP_PATH, AID_NET_BW_STATS,
"IfaceIndexNameMap", false));
- ASSIGN_OR_RETURN(mDozableUidMap,
- setUpBPFMap(sizeof(uint32_t), sizeof(uint8_t), UID_OWNER_MAP_SIZE,
- DOZABLE_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(
+ mDozableUidMap.getOrCreate(UID_OWNER_MAP_SIZE, DOZABLE_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(DOZABLE_UID_MAP_PATH, AID_ROOT, "DozableUidMap", true));
- RETURN_IF_NOT_OK(initialOwnerMap(mDozableUidMap, "DozableUidMap"));
+ RETURN_IF_NOT_OK(initialOwnerMap(mDozableUidMap));
- ASSIGN_OR_RETURN(mStandbyUidMap,
- setUpBPFMap(sizeof(uint32_t), sizeof(uint8_t), UID_OWNER_MAP_SIZE,
- STANDBY_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(
+ mStandbyUidMap.getOrCreate(UID_OWNER_MAP_SIZE, STANDBY_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(STANDBY_UID_MAP_PATH, AID_ROOT, "StandbyUidMap", true));
- RETURN_IF_NOT_OK(initialOwnerMap(mStandbyUidMap, "StandbyUidMap"));
+ RETURN_IF_NOT_OK(initialOwnerMap(mStandbyUidMap));
- ASSIGN_OR_RETURN(mPowerSaveUidMap,
- setUpBPFMap(sizeof(uint32_t), sizeof(uint8_t), UID_OWNER_MAP_SIZE,
- POWERSAVE_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(mPowerSaveUidMap.getOrCreate(UID_OWNER_MAP_SIZE, POWERSAVE_UID_MAP_PATH,
+ BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(POWERSAVE_UID_MAP_PATH, AID_ROOT, "PowerSaveUidMap", true));
- RETURN_IF_NOT_OK(initialOwnerMap(mPowerSaveUidMap, "PowerSaveUidMap"));
+ RETURN_IF_NOT_OK(initialOwnerMap(mPowerSaveUidMap));
- ASSIGN_OR_RETURN(mIfaceStatsMap,
- setUpBPFMap(sizeof(uint32_t), sizeof(struct StatsValue), IFACE_STATS_MAP_SIZE,
- IFACE_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(
+ mIfaceStatsMap.getOrCreate(IFACE_STATS_MAP_SIZE, IFACE_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(IFACE_STATS_MAP_PATH, AID_NET_BW_STATS, "IfaceStatsMap",
false));
return netdutils::status::ok;
@@ -229,7 +231,7 @@
uint64_t sock_cookie = static_cast<uint64_t>(diagmsg.id.idiag_cookie[0]) |
(static_cast<uint64_t>(diagmsg.id.idiag_cookie[1]) << 32);
- deleteMapEntry(mCookieTagMap, &sock_cookie);
+ mCookieTagMap.deleteValue(sock_cookie);
};
expectOk(mSkDestroyListener->subscribe(kSockDiagMsgType, rxHandler));
@@ -294,13 +296,12 @@
// yet. And update the tag if there is already a tag stored. Since the eBPF
// program in kernel only read this map, and is protected by rcu read lock. It
// should be fine to cocurrently update the map while eBPF program is running.
- int res = writeToMapEntry(mCookieTagMap, &sock_cookie, &newKey, BPF_ANY);
- if (res < 0) {
- res = -errno;
- ALOGE("Failed to tag the socket: %s, fd: %d", strerror(errno), mCookieTagMap.get());
+ Status res = mCookieTagMap.writeValue(sock_cookie, newKey, BPF_ANY);
+ if (!isOk(res)) {
+ ALOGE("Failed to tag the socket: %s, fd: %d", strerror(res.code()),
+ mCookieTagMap.getMap().get());
}
-
- return res;
+ return -res.code();
}
int TrafficController::untagSocket(int sockFd) {
@@ -311,17 +312,16 @@
uint64_t sock_cookie = getSocketCookie(sockFd);
if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
- int res = deleteMapEntry(mCookieTagMap, &sock_cookie);
- if (res) {
- res = -errno;
- ALOGE("Failed to untag socket: %s\n", strerror(errno));
+ Status res = mCookieTagMap.deleteValue(sock_cookie);
+ if (!isOk(res)) {
+ ALOGE("Failed to untag socket: %s\n", strerror(res.code()));
}
- return res;
+ return -res.code();
}
int TrafficController::setCounterSet(int counterSetNum, uid_t uid) {
- if (counterSetNum < 0 || counterSetNum >= COUNTERSETS_LIMIT) return -EINVAL;
- int res;
+ if (counterSetNum < 0 || counterSetNum >= OVERFLOW_COUNTERSET) return -EINVAL;
+ Status res;
if (!ebpfSupported) {
if (legacy_setCounterSet(counterSetNum, uid)) return -errno;
return 0;
@@ -330,134 +330,121 @@
// The default counter set for all uid is 0, so deleting the current counterset for that uid
// will automatically set it to 0.
if (counterSetNum == 0) {
- res = deleteMapEntry(mUidCounterSetMap, &uid);
- if (res == 0 || (res == -1 && errno == ENOENT)) {
+ Status res = mUidCounterSetMap.deleteValue(uid);
+ if (isOk(res) || (!isOk(res) && res.code() == ENOENT)) {
return 0;
} else {
- ALOGE("Failed to delete the counterSet: %s\n", strerror(errno));
- return -errno;
+ ALOGE("Failed to delete the counterSet: %s\n", strerror(res.code()));
+ return -res.code();
}
}
-
- res = writeToMapEntry(mUidCounterSetMap, &uid, &counterSetNum, BPF_ANY);
- if (res < 0) {
- res = -errno;
- ALOGE("Failed to set the counterSet: %s, fd: %d", strerror(errno), mUidCounterSetMap.get());
+ uint8_t tmpCounterSetNum = (uint8_t)counterSetNum;
+ res = mUidCounterSetMap.writeValue(uid, tmpCounterSetNum, BPF_ANY);
+ if (!isOk(res)) {
+ ALOGE("Failed to set the counterSet: %s, fd: %d", strerror(res.code()),
+ mUidCounterSetMap.getMap().get());
+ return -res.code();
}
- return res;
+ return 0;
}
int TrafficController::deleteTagData(uint32_t tag, uid_t uid) {
- std::lock_guard<std::mutex> guard(mDeleteStatsMutex);
- int res = 0;
if (!ebpfSupported) {
if (legacy_deleteTagData(tag, uid)) return -errno;
return 0;
}
- uint64_t nonExistentCookie = NONEXISTENT_COOKIE;
// First we go through the cookieTagMap to delete the target uid tag combination. Or delete all
// the tags related to the uid if the tag is 0.
- struct UidTag dummyUidTag;
- auto deleteMatchedCookieEntry = [&uid, &tag](void *key, void *value,
- const base::unique_fd& map_fd) {
- UidTag *tmp_uidtag = (UidTag *) value;
- uint64_t cookie = *(uint64_t *)key;
- if (tmp_uidtag->uid == uid && (tmp_uidtag->tag == tag || tag == 0)) {
- int res = deleteMapEntry(map_fd, &cookie);
- if (res == 0 || (res && errno == ENOENT)) {
- return BPF_DELETED;
+ const auto deleteMatchedCookieEntries = [uid, tag](const uint64_t& key, const UidTag& value,
+ BpfMap<uint64_t, UidTag>& map) {
+ if (value.uid == uid && (value.tag == tag || tag == 0)) {
+ Status res = map.deleteValue(key);
+ if (isOk(res) || (res.code() == ENOENT)) {
+ return netdutils::status::ok;
}
- ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", cookie,
- strerror(errno));
+ ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", key, strerror(res.code()));
}
// Move forward to next cookie in the map.
- return BPF_CONTINUE;
+ return netdutils::status::ok;
};
- bpfIterateMapWithValue(nonExistentCookie, dummyUidTag, mCookieTagMap,
- deleteMatchedCookieEntry);
+ mCookieTagMap.iterateWithValue(deleteMatchedCookieEntries);
// Now we go through the Tag stats map and delete the data entry with correct uid and tag
// combination. Or all tag stats under that uid if the target tag is 0.
- struct StatsKey nonExistentStatsKey = NONEXISTENT_STATSKEY;
- struct StatsValue dummyStatsValue;
- auto deleteMatchedUidTagEntry = [&uid, &tag](void *key, const base::unique_fd& map_fd) {
- StatsKey *keyInfo = (StatsKey *) key;
- if (keyInfo->uid == uid && (keyInfo->tag == tag || tag == 0)) {
- int res = deleteMapEntry(map_fd, key);
- if (res == 0 || (res && (errno == ENOENT))) {
+ const auto deleteMatchedUidTagEntries = [uid, tag](const StatsKey& key,
+ BpfMap<StatsKey, StatsValue>& map) {
+ if (key.uid == uid && (key.tag == tag || tag == 0)) {
+ Status res = map.deleteValue(key);
+ if (isOk(res) || (res.code() == ENOENT)) {
//Entry is deleted, use the current key to get a new nextKey;
- return BPF_DELETED;
+ return netdutils::status::ok;
}
- ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", keyInfo->uid,
- keyInfo->tag, strerror(errno));
+ ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", key.uid, key.tag,
+ strerror(res.code()));
}
- return BPF_CONTINUE;
+ return netdutils::status::ok;
};
- bpfIterateMap(nonExistentStatsKey, dummyStatsValue, mTagStatsMap,
- deleteMatchedUidTagEntry);
+ mTagStatsMap.iterate(deleteMatchedUidTagEntries);
// If the tag is not zero, we already deleted all the data entry required. If tag is 0, we also
// need to delete the stats stored in uidStatsMap and counterSet map.
if (tag != 0) return 0;
- res = deleteMapEntry(mUidCounterSetMap, &uid);
- if (res < 0 && errno != ENOENT) {
- ALOGE("Failed to delete counterSet data(uid=%u, tag=%u): %s\n", uid, tag, strerror(errno));
+ Status res = mUidCounterSetMap.deleteValue(uid);
+ if (!isOk(res) && res.code() != ENOENT) {
+ ALOGE("Failed to delete counterSet data(uid=%u, tag=%u): %s\n", uid, tag,
+ strerror(res.code()));
}
- return bpfIterateMap(nonExistentStatsKey, dummyStatsValue, mUidStatsMap,
- deleteMatchedUidTagEntry);
+ mUidStatsMap.iterate(deleteMatchedUidTagEntries);
+
+ auto deleteAppUidStatsEntry = [uid](const uint32_t& key, BpfMap<uint32_t, StatsValue>& map) {
+ if (key == uid) {
+ Status res = map.deleteValue(key);
+ if (isOk(res) || (res.code() == ENOENT)) {
+ return netdutils::status::ok;
+ }
+ ALOGE("Failed to delete data(uid=%u): %s", key, strerror(res.code()));
+ }
+ return netdutils::status::ok;
+ };
+ mAppUidStatsMap.iterate(deleteAppUidStatsEntry);
+ return 0;
}
int TrafficController::addInterface(const char* name, uint32_t ifaceIndex) {
- int res = 0;
- if (!ebpfSupported) return res;
+ if (!ebpfSupported) return 0;
- char ifaceName[IFNAMSIZ];
+ IfaceValue iface;
if (ifaceIndex == 0) {
- ALOGE("Unknow interface %s(%d)", name, ifaceIndex);
+ ALOGE("Unknown interface %s(%d)", name, ifaceIndex);
return -1;
}
- strlcpy(ifaceName, name, sizeof(ifaceName));
- res = writeToMapEntry(mIfaceIndexNameMap, &ifaceIndex, ifaceName, BPF_ANY);
- if (res) {
- res = -errno;
- ALOGE("Failed to add iface %s(%d): %s", name, ifaceIndex, strerror(errno));
+ strlcpy(iface.name, name, sizeof(IfaceValue));
+ Status res = mIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY);
+ if (!isOk(res)) {
+ ALOGE("Failed to add iface %s(%d): %s", name, ifaceIndex, strerror(res.code()));
+ return -res.code();
}
- return res;
+ return 0;
}
-int TrafficController::updateOwnerMapEntry(const base::unique_fd& map_fd, uid_t uid,
- FirewallRule rule, FirewallType type) {
- int res = 0;
- if (uid == NONEXISTENT_UID) {
- ALOGE("This uid should never exist in maps: %u", uid);
- return -EINVAL;
- }
-
+Status TrafficController::updateOwnerMapEntry(BpfMap<uint32_t, uint8_t>& map, uid_t uid,
+ FirewallRule rule, FirewallType type) {
if (uid == UID_MAP_ENABLED) {
- ALOGE("This uid is reserved for map state");
- return -EINVAL;
+ return statusFromErrno(-EINVAL, "This uid is reserved for map state");
}
if ((rule == ALLOW && type == WHITELIST) || (rule == DENY && type == BLACKLIST)) {
uint8_t flag = (type == WHITELIST) ? BPF_PASS : BPF_DROP;
- res = writeToMapEntry(map_fd, &uid, &flag, BPF_ANY);
- if (res) {
- res = -errno;
- ALOGE("Failed to add owner rule(uid: %u): %s", uid, strerror(errno));
- }
+ RETURN_IF_NOT_OK(map.writeValue(uid, flag, BPF_ANY));
} else if ((rule == ALLOW && type == BLACKLIST) || (rule == DENY && type == WHITELIST)) {
- res = deleteMapEntry(map_fd, &uid);
- if (res) {
- res = -errno;
- ALOGE("Failed to delete owner rule(uid: %u): %s", uid, strerror(errno));
- }
+ RETURN_IF_NOT_OK(map.deleteValue(uid));
} else {
//Cannot happen.
- return -EINVAL;
+ return statusFromErrno(-EINVAL, "");
}
- return res;
+ return netdutils::status::ok;
}
int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule,
@@ -467,54 +454,51 @@
ALOGE("bpf is not set up, should use iptables rule");
return -ENOSYS;
}
+ Status res;
switch (chain) {
case DOZABLE:
- return updateOwnerMapEntry(mDozableUidMap, uid, rule, type);
+ res = updateOwnerMapEntry(mDozableUidMap, uid, rule, type);
+ break;
case STANDBY:
- return updateOwnerMapEntry(mStandbyUidMap, uid, rule, type);
+ res = updateOwnerMapEntry(mStandbyUidMap, uid, rule, type);
+ break;
case POWERSAVE:
- return updateOwnerMapEntry(mPowerSaveUidMap, uid, rule, type);
+ res = updateOwnerMapEntry(mPowerSaveUidMap, uid, rule, type);
+ break;
case NONE:
default:
return -EINVAL;
}
+ if (!isOk(res)) {
+ ALOGE("change uid(%u) rule of %d failed: %s, rule: %d, type: %d", uid, chain,
+ res.msg().c_str(), rule, type);
+ return -res.code();
+ }
+ return 0;
}
-int TrafficController::replaceUidsInMap(const base::unique_fd& map_fd,
- const std::vector<int32_t> &uids,
- FirewallRule rule, FirewallType type) {
+Status TrafficController::replaceUidsInMap(BpfMap<uint32_t, uint8_t>& map,
+ const std::vector<int32_t>& uids, FirewallRule rule,
+ FirewallType type) {
std::set<int32_t> uidSet(uids.begin(), uids.end());
std::vector<uint32_t> uidsToDelete;
- auto getUidsToDelete = [&uidsToDelete, &uidSet](void *key, const base::unique_fd&) {
- uint32_t uid = *(uint32_t *)key;
- if (uid != UID_MAP_ENABLED && uidSet.find((int32_t)uid) == uidSet.end()) {
- uidsToDelete.push_back(uid);
+ auto getUidsToDelete = [&uidsToDelete, &uidSet](const uint32_t& key,
+ const BpfMap<uint32_t, uint8_t>&) {
+ if (key != UID_MAP_ENABLED && uidSet.find((int32_t)key) == uidSet.end()) {
+ uidsToDelete.push_back(key);
}
- return BPF_CONTINUE;
+ return netdutils::status::ok;
};
- uint32_t nonExistentKey = NONEXISTENT_UID;
- uint8_t dummyValue;
- int ret = bpfIterateMap(nonExistentKey, dummyValue, map_fd, getUidsToDelete);
-
- if (ret) return ret;
+ RETURN_IF_NOT_OK(map.iterate(getUidsToDelete));
for(auto uid : uidsToDelete) {
- if(deleteMapEntry(map_fd, &uid)) {
- ret = -errno;
- ALOGE("Delete uid(%u) from owner Map %d failed: %s", uid, map_fd.get(),
- strerror(errno));
- return -errno;
- }
+ RETURN_IF_NOT_OK(map.deleteValue(uid));
}
for (auto uid : uids) {
- ret = updateOwnerMapEntry(map_fd, uid, rule, type);
- if (ret) {
- ALOGE("Failed to add owner rule(uid: %u, map: %d)", uid, map_fd.get());
- return ret;
- }
+ RETURN_IF_NOT_OK(updateOwnerMapEntry(map, uid, rule, type));
}
- return 0;
+ return netdutils::status::ok;
}
int TrafficController::replaceUidOwnerMap(const std::string& name, bool isWhitelist,
@@ -529,20 +513,20 @@
type = BLACKLIST;
rule = DENY;
}
- int ret;
+ Status res;
if (!name.compare(FirewallController::LOCAL_DOZABLE)) {
- ret = replaceUidsInMap(mDozableUidMap, uids, rule, type);
+ res = replaceUidsInMap(mDozableUidMap, uids, rule, type);
} else if (!name.compare(FirewallController::LOCAL_STANDBY)) {
- ret = replaceUidsInMap(mStandbyUidMap, uids, rule, type);
+ res = replaceUidsInMap(mStandbyUidMap, uids, rule, type);
} else if (!name.compare(FirewallController::LOCAL_POWERSAVE)) {
- ret = replaceUidsInMap(mPowerSaveUidMap, uids, rule, type);
+ res = replaceUidsInMap(mPowerSaveUidMap, uids, rule, type);
} else {
ALOGE("unknown chain name: %s", name.c_str());
return -EINVAL;
}
- if (ret) {
- ALOGE("Failed to clean up chain: %s: %s", name.c_str(), strerror(ret));
- return ret;
+ if (!isOk(res)) {
+ ALOGE("Failed to clean up chain: %s: %s", name.c_str(), res.msg().c_str());
+ return -res.code();
}
return 0;
}
@@ -551,25 +535,24 @@
std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
uint32_t keyUid = UID_MAP_ENABLED;
uint8_t mapState = enable ? 1 : 0;
- int ret;
+ Status res;
switch (chain) {
case DOZABLE:
- ret = writeToMapEntry(mDozableUidMap, &keyUid, &mapState, BPF_EXIST);
+ res = mDozableUidMap.writeValue(keyUid, mapState, BPF_EXIST);
break;
case STANDBY:
- ret = writeToMapEntry(mStandbyUidMap, &keyUid, &mapState, BPF_EXIST);
+ res = mStandbyUidMap.writeValue(keyUid, mapState, BPF_EXIST);
break;
case POWERSAVE:
- ret = writeToMapEntry(mPowerSaveUidMap, &keyUid, &mapState, BPF_EXIST);
+ res = mPowerSaveUidMap.writeValue(keyUid, mapState, BPF_EXIST);
break;
default:
return -EINVAL;
}
- if (ret) {
- ret = -errno;
- ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, strerror(errno));
+ if (!isOk(res)) {
+ ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, res.msg().c_str());
}
- return ret;
+ return -res.code();
}
bool TrafficController::checkBpfStatsEnable() {
@@ -587,7 +570,7 @@
return StringPrintf("check Program %s error: %s", path, strerror(errno));
}
-std::string getMapStatus(const unique_fd& map_fd, const char *path) {
+std::string getMapStatus(const base::unique_fd& map_fd, const char* path) {
if (map_fd.get() < 0) {
return StringPrintf("map fd lost");
}
@@ -609,7 +592,6 @@
void TrafficController::dump(DumpWriter& dw, bool verbose) {
std::lock_guard<std::mutex> ownerMapGuard(mOwnerMatchMutex);
- std::lock_guard<std::mutex> statsMapGuard(mDeleteStatsMutex);
dw.incIndent();
dw.println("TrafficController");
@@ -621,21 +603,25 @@
dw.blankline();
dw.println("mCookieTagMap status: %s",
- getMapStatus(mCookieTagMap, COOKIE_TAG_MAP_PATH).c_str());
+ getMapStatus(mCookieTagMap.getMap(), COOKIE_TAG_MAP_PATH).c_str());
dw.println("mUidCounterSetMap status: %s",
- getMapStatus(mUidCounterSetMap, UID_COUNTERSET_MAP_PATH).c_str());
- dw.println("mUidStatsMap status: %s", getMapStatus(mUidStatsMap, UID_STATS_MAP_PATH).c_str());
- dw.println("mTagStatsMap status: %s", getMapStatus(mTagStatsMap, TAG_STATS_MAP_PATH).c_str());
+ getMapStatus(mUidCounterSetMap.getMap(), UID_COUNTERSET_MAP_PATH).c_str());
+ dw.println("mAppUidStatsMap status: %s",
+ getMapStatus(mAppUidStatsMap.getMap(), APP_UID_STATS_MAP_PATH).c_str());
+ dw.println("mUidStatsMap status: %s",
+ getMapStatus(mUidStatsMap.getMap(), UID_STATS_MAP_PATH).c_str());
+ dw.println("mTagStatsMap status: %s",
+ getMapStatus(mTagStatsMap.getMap(), TAG_STATS_MAP_PATH).c_str());
dw.println("mIfaceIndexNameMap status: %s",
- getMapStatus(mIfaceIndexNameMap, IFACE_INDEX_NAME_MAP_PATH).c_str());
+ getMapStatus(mIfaceIndexNameMap.getMap(), IFACE_INDEX_NAME_MAP_PATH).c_str());
dw.println("mIfaceStatsMap status: %s",
- getMapStatus(mIfaceStatsMap, IFACE_STATS_MAP_PATH).c_str());
+ getMapStatus(mIfaceStatsMap.getMap(), IFACE_STATS_MAP_PATH).c_str());
dw.println("mDozableUidMap status: %s",
- getMapStatus(mDozableUidMap, DOZABLE_UID_MAP_PATH).c_str());
+ getMapStatus(mDozableUidMap.getMap(), DOZABLE_UID_MAP_PATH).c_str());
dw.println("mStandbyUidMap status: %s",
- getMapStatus(mStandbyUidMap, STANDBY_UID_MAP_PATH).c_str());
+ getMapStatus(mStandbyUidMap.getMap(), STANDBY_UID_MAP_PATH).c_str());
dw.println("mPowerSaveUidMap status: %s",
- getMapStatus(mPowerSaveUidMap, POWERSAVE_UID_MAP_PATH).c_str());
+ getMapStatus(mPowerSaveUidMap.getMap(), POWERSAVE_UID_MAP_PATH).c_str());
dw.blankline();
dw.println("Cgroup ingress program status: %s",
@@ -655,124 +641,119 @@
// Print CookieTagMap content.
dumpBpfMap("mCookieTagMap", dw, "");
- const uint64_t nonExistentCookie = NONEXISTENT_COOKIE;
- UidTag dummyUidTag;
- auto printCookieTagInfo = [&dw](void *key, void *value, const base::unique_fd&) {
- UidTag uidTagEntry = *(UidTag *) value;
- uint64_t cookie = *(uint64_t *) key;
- dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", cookie, uidTagEntry.tag, uidTagEntry.uid);
- return BPF_CONTINUE;
+ const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTag& value,
+ const BpfMap<uint64_t, UidTag>&) {
+ dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", key, value.tag, value.uid);
+ return netdutils::status::ok;
};
- int ret = bpfIterateMapWithValue(nonExistentCookie, dummyUidTag, mCookieTagMap,
- printCookieTagInfo);
- if (ret) {
- dw.println("mCookieTagMap print end with error: %s", strerror(-ret));
+ Status res = mCookieTagMap.iterateWithValue(printCookieTagInfo);
+ if (!isOk(res)) {
+ dw.println("mCookieTagMap print end with error: %s", res.msg().c_str());
}
// Print UidCounterSetMap Content
dumpBpfMap("mUidCounterSetMap", dw, "");
- const uint32_t nonExistentUid = NONEXISTENT_UID;
- uint32_t dummyUidInfo;
- auto printUidInfo = [&dw](void *key, void *value, const base::unique_fd&) {
- uint8_t setting = *(uint8_t *) value;
- uint32_t uid = *(uint32_t *) key;
- dw.println("%u %u", uid, setting);
- return BPF_CONTINUE;
+ const auto printUidInfo = [&dw](const uint32_t& key, const uint8_t& value,
+ const BpfMap<uint32_t, uint8_t>&) {
+ dw.println("%u %u", key, value);
+ return netdutils::status::ok;
};
- ret = bpfIterateMapWithValue(nonExistentUid, dummyUidInfo, mUidCounterSetMap, printUidInfo);
- if (ret) {
- dw.println("mUidCounterSetMap print end with error: %s", strerror(-ret));
+ res = mUidCounterSetMap.iterateWithValue(printUidInfo);
+ if (!isOk(res)) {
+ dw.println("mUidCounterSetMap print end with error: %s", res.msg().c_str());
+ }
+
+ // Print AppUidStatsMap content
+ std::string appUidStatsHeader = StringPrintf("uid rxBytes rxPackets txBytes txPackets");
+ dumpBpfMap("mAppUidStatsMap:", dw, appUidStatsHeader);
+ auto printAppUidStatsInfo = [&dw](const uint32_t& key, const StatsValue& value,
+ const BpfMap<uint32_t, StatsValue>&) {
+ dw.println("%u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, value.rxBytes,
+ value.rxPackets, value.txBytes, value.txPackets);
+ return netdutils::status::ok;
+ };
+ res = mAppUidStatsMap.iterateWithValue(printAppUidStatsInfo);
+ if (!res.ok()) {
+ dw.println("mAppUidStatsMap print end with error: %s", res.msg().c_str());
}
// Print uidStatsMap content
std::string statsHeader = StringPrintf("ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes"
" rxPackets txBytes txPackets");
dumpBpfMap("mUidStatsMap", dw, statsHeader);
- const struct StatsKey nonExistentStatsKey = NONEXISTENT_STATSKEY;
- struct StatsValue dummyStatsValue;
- auto printStatsInfo = [&dw, this](void *key, void *value, const base::unique_fd&) {
- StatsValue statsEntry = *(StatsValue *) value;
- StatsKey keyInfo = *(StatsKey *) key;
- char ifname[IFNAMSIZ];
- uint32_t ifIndex = keyInfo.ifaceIndex;
- if (bpf::findMapEntry(mIfaceIndexNameMap, &ifIndex, &ifname) < 0) {
- strlcpy(ifname, "unknown", sizeof(ifname));
+ const auto printStatsInfo = [&dw, this](const StatsKey& key, const StatsValue& value,
+ const BpfMap<StatsKey, StatsValue>&) {
+ uint32_t ifIndex = key.ifaceIndex;
+ auto ifname = mIfaceIndexNameMap.readValue(ifIndex);
+ if (!isOk(ifname)) {
+ strlcpy(ifname.value().name, "unknown", sizeof(IfaceValue));
}
- dw.println("%u %s 0x%x %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifIndex, ifname,
- keyInfo.tag, keyInfo.uid, keyInfo.counterSet, statsEntry.rxBytes,
- statsEntry.rxPackets, statsEntry.txBytes, statsEntry.txPackets);
- return BPF_CONTINUE;
+ dw.println("%u %s 0x%x %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifIndex,
+ ifname.value().name, key.tag, key.uid, key.counterSet, value.rxBytes,
+ value.rxPackets, value.txBytes, value.txPackets);
+ return netdutils::status::ok;
};
- ret = bpfIterateMapWithValue(nonExistentStatsKey, dummyStatsValue, mUidStatsMap,
- printStatsInfo);
- if (ret) {
- dw.println("mUidStatsMap print end with error: %s", strerror(-ret));
+ res = mUidStatsMap.iterateWithValue(printStatsInfo);
+ if (!isOk(res)) {
+ dw.println("mUidStatsMap print end with error: %s", res.msg().c_str());
}
// Print TagStatsMap content.
dumpBpfMap("mTagStatsMap", dw, statsHeader);
- ret = bpfIterateMapWithValue(nonExistentStatsKey, dummyStatsValue, mTagStatsMap,
- printStatsInfo);
- if (ret) {
- dw.println("mTagStatsMap print end with error: %s", strerror(-ret));
+ res = mTagStatsMap.iterateWithValue(printStatsInfo);
+ if (!isOk(res)) {
+ dw.println("mTagStatsMap print end with error: %s", res.msg().c_str());
}
// Print ifaceIndexToNameMap content.
dumpBpfMap("mIfaceIndexNameMap", dw, "");
- uint32_t nonExistentIface = NONEXISTENT_IFACE_STATS_KEY;
- char dummyIface[IFNAMSIZ];
- auto printIfaceNameInfo = [&dw](void *key, void *value, const base::unique_fd&) {
- char *ifname = (char *) value;
- uint32_t ifaceIndex = *(uint32_t *)key;
- dw.println("ifaceIndex=%u ifaceName=%s", ifaceIndex, ifname);
- return BPF_CONTINUE;
+ const auto printIfaceNameInfo = [&dw](const uint32_t& key, const IfaceValue& value,
+ const BpfMap<uint32_t, IfaceValue>&) {
+ const char* ifname = value.name;
+ dw.println("ifaceIndex=%u ifaceName=%s", key, ifname);
+ return netdutils::status::ok;
};
- ret = bpfIterateMapWithValue(nonExistentIface, dummyIface, mIfaceIndexNameMap,
- printIfaceNameInfo);
- if (ret) {
- dw.println("mIfaceIndexNameMap print end with error: %s", strerror(-ret));
+ res = mIfaceIndexNameMap.iterateWithValue(printIfaceNameInfo);
+ if (!isOk(res)) {
+ dw.println("mIfaceIndexNameMap print end with error: %s", res.msg().c_str());
}
// Print ifaceStatsMap content
std::string ifaceStatsHeader = StringPrintf("ifaceIndex ifaceName rxBytes rxPackets txBytes"
" txPackets");
dumpBpfMap("mIfaceStatsMap:", dw, ifaceStatsHeader);
- auto printIfaceStatsInfo = [&dw, this] (void *key, void *value, const base::unique_fd&) {
- StatsValue statsEntry = *(StatsValue *) value;
- uint32_t ifaceIndex = *(uint32_t *) key;
- char ifname[IFNAMSIZ];
- if (bpf::findMapEntry(mIfaceIndexNameMap, key, ifname) < 0) {
- strlcpy(ifname, "unknown", sizeof(ifname));
+ const auto printIfaceStatsInfo = [&dw, this](const uint32_t& key, const StatsValue& value,
+ const BpfMap<uint32_t, StatsValue>&) {
+ auto ifname = mIfaceIndexNameMap.readValue(key);
+ if (!isOk(ifname)) {
+ strlcpy(ifname.value().name, "unknown", sizeof(IfaceValue));
}
- dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifaceIndex, ifname,
- statsEntry.rxBytes, statsEntry.rxPackets, statsEntry.txBytes,
- statsEntry.txPackets);
- return BPF_CONTINUE;
+ dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, ifname.value().name,
+ value.rxBytes, value.rxPackets, value.txBytes, value.txPackets);
+ return netdutils::status::ok;
};
- ret = bpfIterateMapWithValue(nonExistentIface, dummyStatsValue, mIfaceStatsMap,
- printIfaceStatsInfo);
- if (ret) {
- dw.println("mIfaceStatsMap print end with error: %s", strerror(-ret));
+ res = mIfaceStatsMap.iterateWithValue(printIfaceStatsInfo);
+ if (!isOk(res)) {
+ dw.println("mIfaceStatsMap print end with error: %s", res.msg().c_str());
}
// Print owner match uid maps
- uint8_t dummyOwnerInfo;
dumpBpfMap("mDozableUidMap", dw, "");
- ret = bpfIterateMapWithValue(nonExistentUid, dummyOwnerInfo, mDozableUidMap, printUidInfo);
- if (ret) {
- dw.println("mDozableUidMap print end with error: %s", strerror(-ret));
+ res = mDozableUidMap.iterateWithValue(printUidInfo);
+ if (!isOk(res)) {
+ dw.println("mDozableUidMap print end with error: %s", res.msg().c_str());
}
dumpBpfMap("mStandbyUidMap", dw, "");
- ret = bpfIterateMapWithValue(nonExistentUid, dummyOwnerInfo, mStandbyUidMap, printUidInfo);
- if (ret) {
- dw.println("mDozableUidMap print end with error: %s", strerror(-ret));
+ res = mStandbyUidMap.iterateWithValue(printUidInfo);
+ if (!isOk(res)) {
+ dw.println("mDozableUidMap print end with error: %s", res.msg().c_str());
}
dumpBpfMap("mPowerSaveUidMap", dw, "");
- ret = bpfIterateMapWithValue(nonExistentUid, dummyOwnerInfo, mPowerSaveUidMap, printUidInfo);
- if (ret) {
- dw.println("mDozableUidMap print end with error: %s", strerror(-ret));
+ res = mPowerSaveUidMap.iterateWithValue(printUidInfo);
+ if (!isOk(res)) {
+ dw.println("mDozableUidMap print end with error: %s", res.msg().c_str());
}
dw.decIndent();
diff --git a/server/TrafficController.h b/server/TrafficController.h
index b810bf8..79f7d14 100644
--- a/server/TrafficController.h
+++ b/server/TrafficController.h
@@ -25,26 +25,13 @@
#include "Network.h"
#include "android-base/thread_annotations.h"
#include "android-base/unique_fd.h"
+#include "bpf/BpfMap.h"
-// Since we cannot garbage collect the stats map since device boot, we need to make these maps as
-// large as possible. The current rlimit of MEM_LOCK allows at most 10000 map entries for each
-// stats map. In the old qtaguid module, we don't have a total limit for data entries but only have
-// limitation of tags each uid can have. (default is 1024 in kernel);
-// cookie_uid_map: key: 8 bytes, value: 8 bytes, total:10000*8*2 bytes = 160Kbytes
-// uid_counter_set_map: key: 4 bytes, value: 4 bytes, total:10000*4*2 bytes = 80Kbytes
-// uid_stats_map: key: 16 bytes, value: 32 bytes, total:10000*16+10000*32 bytes = 480Kbytes
-// tag_stats_map: key: 16 bytes, value: 32 bytes, total:10000*16+10000*32 bytes = 480Kbytes
-// iface_index_name_map:key: 4 bytes, value: 32 bytes, total:10000*36 bytes = 360Kbytes
-// total: 1560Kbytes
-constexpr const int COOKIE_UID_MAP_SIZE = 10000;
-constexpr const int UID_COUNTERSET_MAP_SIZE = 10000;
-constexpr const int UID_STATS_MAP_SIZE = 10000;
-constexpr const int TAG_STATS_MAP_SIZE = 10000;
-constexpr const int IFACE_INDEX_NAME_MAP_SIZE = 1000;
-constexpr const int IFACE_STATS_MAP_SIZE = 1000;
-constexpr const int UID_OWNER_MAP_SIZE = 10000;
-
-constexpr const int COUNTERSETS_LIMIT = 2;
+using android::bpf::BpfMap;
+using android::bpf::IfaceValue;
+using android::bpf::StatsKey;
+using android::bpf::StatsValue;
+using android::bpf::UidTag;
namespace android {
namespace net {
@@ -110,13 +97,14 @@
int replaceUidOwnerMap(const std::string& name, bool isWhitelist,
const std::vector<int32_t>& uids);
- int updateOwnerMapEntry(const base::unique_fd& map_fd, uid_t uid, FirewallRule rule,
- FirewallType type);
+ netdutils::Status updateOwnerMapEntry(BpfMap<uint32_t, uint8_t>& map, uid_t uid,
+ FirewallRule rule, FirewallType type);
void dump(DumpWriter& dw, bool verbose);
- int replaceUidsInMap(const base::unique_fd& map_fd, const std::vector<int32_t> &uids,
- FirewallRule rule, FirewallType type);
+ netdutils::Status replaceUidsInMap(BpfMap<uint32_t, uint8_t>& map,
+ const std::vector<int32_t>& uids, FirewallRule rule,
+ FirewallType type);
static const String16 DUMP_KEYWORD;
@@ -133,7 +121,7 @@
* Map Key: uint64_t socket cookie
* Map Value: struct UidTag, contains a uint32 uid and a uint32 tag.
*/
- base::unique_fd mCookieTagMap;
+ BpfMap<uint64_t, UidTag> mCookieTagMap;
/*
* mUidCounterSetMap: Store the counterSet of a specific uid.
@@ -141,7 +129,14 @@
* Map Value: uint32 counterSet specifies if the traffic is a background
* or foreground traffic.
*/
- base::unique_fd mUidCounterSetMap;
+ BpfMap<uint32_t, uint8_t> mUidCounterSetMap;
+
+ /*
+ * mAppUidStatsMap: Store the total traffic stats for a uid regardless of
+ * tag, counterSet and iface. The stats is used by TrafficStats.getUidStats
+ * API to return persistent stats for a specific uid since device boot.
+ */
+ BpfMap<uint32_t, StatsValue> mAppUidStatsMap;
/*
* mUidStatsMap: Store the traffic statistics for a specific combination of
@@ -153,7 +148,7 @@
* Map Value: struct Stats, contains packet count and byte count of each
* transport protocol on egress and ingress direction.
*/
- base::unique_fd mUidStatsMap GUARDED_BY(mDeleteStatsMutex);
+ BpfMap<StatsKey, StatsValue> mUidStatsMap;
/*
* mTagStatsMap: Store the traffic statistics for a specific combination of
@@ -164,39 +159,38 @@
* Map Value: struct Stats, contains packet count and byte count of each
* transport protocol on egress and ingress direction.
*/
- base::unique_fd mTagStatsMap GUARDED_BY(mDeleteStatsMutex);
-
+ BpfMap<StatsKey, StatsValue> mTagStatsMap;
/*
* mIfaceIndexNameMap: Store the index name pair of each interface show up
* on the device since boot. The interface index is used by the eBPF program
* to correctly match the iface name when receiving a packet.
*/
- base::unique_fd mIfaceIndexNameMap;
+ BpfMap<uint32_t, IfaceValue> mIfaceIndexNameMap;
/*
* mIfaceStataMap: Store per iface traffic stats gathered from xt_bpf
* filter.
*/
- base::unique_fd mIfaceStatsMap;
+ BpfMap<uint32_t, StatsValue> mIfaceStatsMap;
/*
* mDozableUidMap: Store uids that have related rules in dozable mode owner match
* chain.
*/
- base::unique_fd mDozableUidMap GUARDED_BY(mOwnerMatchMutex);
+ BpfMap<uint32_t, uint8_t> mDozableUidMap GUARDED_BY(mOwnerMatchMutex);
/*
* mStandbyUidMap: Store uids that have related rules in standby mode owner match
* chain.
*/
- base::unique_fd mStandbyUidMap GUARDED_BY(mOwnerMatchMutex);
+ BpfMap<uint32_t, uint8_t> mStandbyUidMap GUARDED_BY(mOwnerMatchMutex);
/*
* mPowerSaveUidMap: Store uids that have related rules in power save mode owner match
* chain.
*/
- base::unique_fd mPowerSaveUidMap GUARDED_BY(mOwnerMatchMutex);
+ BpfMap<uint32_t, uint8_t> mPowerSaveUidMap GUARDED_BY(mOwnerMatchMutex);
std::unique_ptr<NetlinkListenerInterface> mSkDestroyListener;
@@ -204,18 +198,6 @@
std::mutex mOwnerMatchMutex;
- // When aquiring both mOwnerMatchMutex and mDeleteStatsMutex,
- // mOwnerMatchMutex must be grabbed first to prevent protential deadlock.
- // This lock need to be hold when deleting from any stats map which we
- // can iterate which are uidStatsMap and tagStatsMap. We don't need this
- // lock to guard mUidCounterSetMap because we always directly look up /
- // write / delete the map by uid. Also we don't need this lock for
- // mCookieTagMap since the only time we need to iterate the map is
- // deleteTagStats and we don't care if we failed and started from the
- // beginning, since we will eventually scan through the map and delete all
- // target entries.
- std::mutex mDeleteStatsMutex;
-
netdutils::Status loadAndAttachProgram(bpf_attach_type type, const char* path, const char* name,
base::unique_fd& cg_fd);
diff --git a/server/TrafficControllerTest.cpp b/server/TrafficControllerTest.cpp
index 187014f..a354f83 100644
--- a/server/TrafficControllerTest.cpp
+++ b/server/TrafficControllerTest.cpp
@@ -33,6 +33,9 @@
#include <android-base/strings.h>
#include <netdutils/MockSyscalls.h>
+#include "netdutils/Status.h"
+#include "netdutils/StatusOr.h"
+
#include "FirewallController.h"
#include "TrafficController.h"
#include "bpf/BpfUtils.h"
@@ -50,70 +53,78 @@
namespace net {
using base::unique_fd;
+using netdutils::isOk;
+using netdutils::Status;
using netdutils::status::ok;
+using netdutils::StatusOr;
constexpr int TEST_MAP_SIZE = 10;
constexpr uid_t TEST_UID = 10086;
constexpr uid_t TEST_UID2 = 54321;
constexpr uid_t TEST_UID3 = 98765;
constexpr uint32_t TEST_TAG = 42;
-constexpr int TEST_COUNTERSET = 1;
-constexpr int DEFAULT_COUNTERSET = 0;
+constexpr uint32_t TEST_COUNTERSET = 1;
+constexpr uint32_t DEFAULT_COUNTERSET = 0;
class TrafficControllerTest : public ::testing::Test {
protected:
TrafficControllerTest() {}
TrafficController mTc;
- unique_fd mFakeCookieTagMap;
- unique_fd mFakeUidCounterSetMap;
- unique_fd mFakeUidStatsMap;
- unique_fd mFakeTagStatsMap;
- unique_fd mFakeDozableUidMap;
- unique_fd mFakeStandbyUidMap;
- unique_fd mFakePowerSaveUidMap;
+ BpfMap<uint64_t, UidTag> mFakeCookieTagMap;
+ BpfMap<uint32_t, uint8_t> mFakeUidCounterSetMap;
+ BpfMap<uint32_t, StatsValue> mFakeAppUidStatsMap;
+ BpfMap<StatsKey, StatsValue> mFakeUidStatsMap;
+ BpfMap<StatsKey, StatsValue> mFakeTagStatsMap;
+ BpfMap<uint32_t, uint8_t> mFakeDozableUidMap;
+ BpfMap<uint32_t, uint8_t> mFakeStandbyUidMap;
+ BpfMap<uint32_t, uint8_t> mFakePowerSaveUidMap;
void SetUp() {
std::lock_guard<std::mutex> ownerGuard(mTc.mOwnerMatchMutex);
- std::lock_guard<std::mutex> statsGuard(mTc.mDeleteStatsMutex);
SKIP_IF_BPF_NOT_SUPPORTED;
- mFakeCookieTagMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(uint64_t),
- sizeof(struct UidTag), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeCookieTagMap);
+ mFakeCookieTagMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint64_t),
+ sizeof(struct UidTag), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeCookieTagMap.getMap());
- mFakeUidCounterSetMap = unique_fd(
- createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint32_t), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeUidCounterSetMap);
+ mFakeUidCounterSetMap.reset(
+ createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint8_t), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeUidCounterSetMap.getMap());
- mFakeUidStatsMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
- sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeUidStatsMap);
+ mFakeAppUidStatsMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t),
+ sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeAppUidStatsMap.getMap());
- mFakeTagStatsMap = unique_fd(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
- sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeTagStatsMap);
+ mFakeUidStatsMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
+ sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeUidStatsMap.getMap());
- mFakeDozableUidMap = unique_fd(
- createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint32_t), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeDozableUidMap);
+ mFakeTagStatsMap.reset(createMap(BPF_MAP_TYPE_HASH, sizeof(struct StatsKey),
+ sizeof(struct StatsValue), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeTagStatsMap.getMap());
- mFakeStandbyUidMap = unique_fd(
- createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint32_t), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakeStandbyUidMap);
+ mFakeDozableUidMap.reset(
+ createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint8_t), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeDozableUidMap.getMap());
- mFakePowerSaveUidMap = unique_fd(
- createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint32_t), TEST_MAP_SIZE, 0));
- ASSERT_LE(0, mFakePowerSaveUidMap);
+ mFakeStandbyUidMap.reset(
+ createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint8_t), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakeStandbyUidMap.getMap());
+
+ mFakePowerSaveUidMap.reset(
+ createMap(BPF_MAP_TYPE_HASH, sizeof(uint32_t), sizeof(uint8_t), TEST_MAP_SIZE, 0));
+ ASSERT_LE(0, mFakePowerSaveUidMap.getMap());
// Make sure trafficController use the eBPF code path.
mTc.ebpfSupported = true;
- mTc.mCookieTagMap.reset(mFakeCookieTagMap);
- mTc.mUidCounterSetMap.reset(mFakeUidCounterSetMap);
- mTc.mUidStatsMap.reset(mFakeUidStatsMap);
- mTc.mTagStatsMap.reset(mFakeTagStatsMap);
- mTc.mDozableUidMap.reset(mFakeDozableUidMap);
- mTc.mStandbyUidMap.reset(mFakeStandbyUidMap);
- mTc.mPowerSaveUidMap.reset(mFakePowerSaveUidMap);
+ mTc.mCookieTagMap.reset(mFakeCookieTagMap.getMap());
+ mTc.mUidCounterSetMap.reset(mFakeUidCounterSetMap.getMap());
+ mTc.mAppUidStatsMap.reset(mFakeAppUidStatsMap.getMap());
+ mTc.mUidStatsMap.reset(mFakeUidStatsMap.getMap());
+ mTc.mTagStatsMap.reset(mFakeTagStatsMap.getMap());
+ mTc.mDozableUidMap.reset(mFakeDozableUidMap.getMap());
+ mTc.mStandbyUidMap.reset(mFakeStandbyUidMap.getMap());
+ mTc.mPowerSaveUidMap.reset(mFakePowerSaveUidMap.getMap());
}
int setUpSocketAndTag(int protocol, uint64_t* cookie, uint32_t tag, uid_t uid) {
@@ -126,84 +137,80 @@
}
void expectUidTag(uint64_t cookie, uid_t uid, uint32_t tag) {
- struct UidTag tagResult;
- EXPECT_EQ(0, findMapEntry(mFakeCookieTagMap, &cookie, &tagResult));
- EXPECT_EQ(uid, tagResult.uid);
- EXPECT_EQ(tag, tagResult.tag);
+ StatusOr<UidTag> tagResult = mFakeCookieTagMap.readValue(cookie);
+ EXPECT_TRUE(isOk(tagResult));
+ EXPECT_EQ(uid, tagResult.value().uid);
+ EXPECT_EQ(tag, tagResult.value().tag);
}
- void expectNoTag(uint64_t cookie) {
- struct UidTag tagResult;
- EXPECT_EQ(-1, findMapEntry(mFakeCookieTagMap, &cookie, &tagResult));
- }
+ void expectNoTag(uint64_t cookie) { EXPECT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie))); }
- void expectTagMapEmpty() {
- uint64_t invalidCookie = NONEXISTENT_COOKIE;
- uint64_t cookie;
- EXPECT_EQ(-1, getNextMapKey(mFakeCookieTagMap, &invalidCookie, &cookie));
- }
+ void expectTagMapEmpty() { EXPECT_FALSE(isOk(mFakeCookieTagMap.getFirstKey())); }
- void populateFakeStats(uint64_t cookie, uid_t uid, uint32_t tag, StatsKey* key) {
+ void populateFakeStats(uint64_t cookie, uint32_t uid, uint32_t tag, StatsKey* key) {
UidTag cookieMapkey = {.uid = (uint32_t)uid, .tag = tag};
- EXPECT_EQ(0, writeToMapEntry(mFakeCookieTagMap, &cookie, &cookieMapkey, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeCookieTagMap.writeValue(cookie, cookieMapkey, BPF_ANY)));
*key = {.uid = uid, .tag = tag, .counterSet = TEST_COUNTERSET, .ifaceIndex = 1};
StatsValue statsMapValue = {.rxPackets = 1, .rxBytes = 100};
- int counterSet = TEST_COUNTERSET;
- EXPECT_EQ(0, writeToMapEntry(mFakeUidCounterSetMap, &uid, &counterSet, BPF_ANY));
- EXPECT_EQ(0, writeToMapEntry(mFakeTagStatsMap, key, &statsMapValue, BPF_ANY));
+ uint8_t counterSet = TEST_COUNTERSET;
+ EXPECT_TRUE(isOk(mFakeUidCounterSetMap.writeValue(uid, counterSet, BPF_ANY)));
+ EXPECT_TRUE(isOk(mFakeTagStatsMap.writeValue(*key, statsMapValue, BPF_ANY)));
key->tag = 0;
- EXPECT_EQ(0, writeToMapEntry(mFakeUidStatsMap, key, &statsMapValue, BPF_ANY));
+ EXPECT_TRUE(isOk(mFakeUidStatsMap.writeValue(*key, statsMapValue, BPF_ANY)));
+ EXPECT_TRUE(isOk(mFakeAppUidStatsMap.writeValue(uid, statsMapValue, BPF_ANY)));
// put tag information back to statsKey
key->tag = tag;
}
- void checkUidOwnerRuleForChain(ChildChain chain, const unique_fd& targetMap) {
+ void checkUidOwnerRuleForChain(ChildChain chain, BpfMap<uint32_t, uint8_t>& targetMap) {
uint32_t uid = TEST_UID;
EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, BLACKLIST));
- uint8_t value;
- EXPECT_EQ(0, findMapEntry(targetMap, &uid, &value));
- EXPECT_EQ((uint8_t)BPF_DROP, value);
+ StatusOr<uint8_t> value = targetMap.readValue(uid);
+ EXPECT_TRUE(isOk(value));
+ EXPECT_EQ(BPF_DROP, value.value());
uid = TEST_UID2;
EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, WHITELIST));
- EXPECT_EQ(0, findMapEntry(targetMap, &uid, &value));
- EXPECT_EQ((uint8_t)BPF_PASS, value);
+ value = targetMap.readValue(uid);
+ EXPECT_TRUE(isOk(value));
+ EXPECT_EQ(BPF_PASS, value.value());
EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, DENY, WHITELIST));
- EXPECT_EQ(-1, findMapEntry(targetMap, &uid, &value));
- EXPECT_EQ(ENOENT, errno);
+ value = targetMap.readValue(uid);
+ EXPECT_FALSE(isOk(value));
+ EXPECT_EQ(ENOENT, value.status().code());
uid = TEST_UID;
EXPECT_EQ(0, mTc.changeUidOwnerRule(chain, uid, ALLOW, BLACKLIST));
- EXPECT_EQ(-1, findMapEntry(targetMap, &uid, &value));
- EXPECT_EQ(ENOENT, errno);
+ value = targetMap.readValue(uid);
+ EXPECT_FALSE(isOk(value));
+ EXPECT_EQ(ENOENT, value.status().code());
uid = TEST_UID3;
EXPECT_EQ(-ENOENT, mTc.changeUidOwnerRule(chain, uid, ALLOW, BLACKLIST));
- EXPECT_EQ(-1, findMapEntry(targetMap, &uid, &value));
- EXPECT_EQ(ENOENT, errno);
+ value = targetMap.readValue(uid);
+ EXPECT_FALSE(isOk(value));
+ EXPECT_EQ(ENOENT, value.status().code());
}
void checkEachUidValue(const std::vector<int32_t>& uids, const uint8_t expectValue,
- const unique_fd& targetMap) {
- uint8_t value;
- for (auto uid : uids) {
- EXPECT_EQ(0, findMapEntry(targetMap, &uid, &value));
- EXPECT_EQ((uint8_t)expectValue, value);
+ BpfMap<uint32_t, uint8_t>& targetMap) {
+ for (uint32_t uid : uids) {
+ StatusOr<uint8_t> value = targetMap.readValue(uid);
+ EXPECT_TRUE(isOk(value));
+ EXPECT_EQ(expectValue, value.value());
}
std::set<uint32_t> uidSet(uids.begin(), uids.end());
- auto checkNoOtherUid = [&uidSet](void *key, const base::unique_fd&) {
- int32_t uid = *(int32_t *)key;
- EXPECT_NE(uidSet.end(), uidSet.find(uid));
- return BPF_CONTINUE;
+ const auto checkNoOtherUid = [&uidSet](const int32_t& key,
+ const BpfMap<uint32_t, uint8_t>&) {
+ EXPECT_NE(uidSet.end(), uidSet.find(key));
+ return netdutils::status::ok;
};
- uint32_t nonExistentKey = NONEXISTENT_UID;
- uint8_t dummyValue;
- EXPECT_EQ(0, bpfIterateMap(nonExistentKey, dummyValue, targetMap, checkNoOtherUid));
+ EXPECT_TRUE(isOk(targetMap.iterate(checkNoOtherUid)));
}
void checkUidMapReplace(const std::string& name, const std::vector<int32_t>& uids,
- const unique_fd& targetMap) {
+ BpfMap<uint32_t, uint8_t>& targetMap) {
bool isWhitelist = true;
EXPECT_EQ(0, mTc.replaceUidOwnerMap(name, isWhitelist, uids));
checkEachUidValue(uids, BPF_PASS, targetMap);
@@ -214,10 +221,15 @@
}
void TearDown() {
+ std::lock_guard<std::mutex> ownerGuard(mTc.mOwnerMatchMutex);
mFakeCookieTagMap.reset();
mFakeUidCounterSetMap.reset();
+ mFakeAppUidStatsMap.reset();
mFakeUidStatsMap.reset();
mFakeTagStatsMap.reset();
+ mTc.mDozableUidMap.reset();
+ mTc.mStandbyUidMap.reset();
+ mTc.mPowerSaveUidMap.reset();
}
};
@@ -254,8 +266,7 @@
ASSERT_EQ(0, mTc.untagSocket(v4socket1));
expectNoTag(sockCookie1);
expectUidTag(sockCookie2, TEST_UID, TEST_TAG);
- uint64_t cookieResult;
- ASSERT_EQ(-1, getNextMapKey(mFakeCookieTagMap, &sockCookie2, &cookieResult));
+ ASSERT_FALSE(isOk(mFakeCookieTagMap.getNextKey(sockCookie2)));
}
TEST_F(TrafficControllerTest, TestTagSocketV6) {
@@ -292,24 +303,21 @@
ASSERT_EQ(0, mTc.setCounterSet(TEST_COUNTERSET, TEST_UID));
uid_t uid = TEST_UID;
- int counterSetResult;
- ASSERT_EQ(0, findMapEntry(mFakeUidCounterSetMap, &uid, &counterSetResult));
- ASSERT_EQ(TEST_COUNTERSET, counterSetResult);
+ StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_TRUE(isOk(counterSetResult));
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
ASSERT_EQ(0, mTc.setCounterSet(DEFAULT_COUNTERSET, TEST_UID));
- ASSERT_EQ(-1, findMapEntry(mFakeUidCounterSetMap, &uid, &counterSetResult));
- uid = TEST_UID;
- ASSERT_EQ(-1, getNextMapKey(mFakeUidCounterSetMap, &uid, &counterSetResult));
+ ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid)));
+ ASSERT_FALSE(isOk(mFakeUidCounterSetMap.getFirstKey()));
}
TEST_F(TrafficControllerTest, TestSetInvalidCounterSet) {
SKIP_IF_BPF_NOT_SUPPORTED;
- ASSERT_GT(0, mTc.setCounterSet(COUNTERSETS_LIMIT, TEST_UID));
+ ASSERT_GT(0, mTc.setCounterSet(OVERFLOW_COUNTERSET, TEST_UID));
uid_t uid = TEST_UID;
- int counterSetResult;
- ASSERT_EQ(-1, findMapEntry(mFakeUidCounterSetMap, &uid, &counterSetResult));
- uid = TEST_UID;
- ASSERT_EQ(-1, getNextMapKey(mFakeUidCounterSetMap, &uid, &counterSetResult));
+ ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid)));
+ ASSERT_FALSE(isOk(mFakeUidCounterSetMap.getFirstKey()));
}
TEST_F(TrafficControllerTest, TestDeleteTagData) {
@@ -321,17 +329,20 @@
StatsKey tagStatsMapKey;
populateFakeStats(cookie, uid, tag, &tagStatsMapKey);
ASSERT_EQ(0, mTc.deleteTagData(TEST_TAG, TEST_UID));
- UidTag cookieMapkey;
- ASSERT_EQ(-1, findMapEntry(mFakeCookieTagMap, &cookie, &cookieMapkey));
- int counterSetResult;
- ASSERT_EQ(0, findMapEntry(mFakeUidCounterSetMap, &uid, &counterSetResult));
- ASSERT_EQ(TEST_COUNTERSET, counterSetResult);
- StatsValue statsMapResult;
- ASSERT_EQ(-1, findMapEntry(mFakeTagStatsMap, &tagStatsMapKey, &statsMapResult));
+ ASSERT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie)));
+ StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_TRUE(isOk(counterSetResult));
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
+ ASSERT_FALSE(isOk(mFakeTagStatsMap.readValue(tagStatsMapKey)));
tagStatsMapKey.tag = 0;
- ASSERT_EQ(0, findMapEntry(mFakeUidStatsMap, &tagStatsMapKey, &statsMapResult));
- ASSERT_EQ((uint64_t)1, statsMapResult.rxPackets);
- ASSERT_EQ((uint64_t)100, statsMapResult.rxBytes);
+ StatusOr<StatsValue> statsMapResult = mFakeUidStatsMap.readValue(tagStatsMapKey);
+ ASSERT_TRUE(isOk(statsMapResult));
+ ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
+ auto appStatsResult = mFakeAppUidStatsMap.readValue(TEST_UID);
+ ASSERT_TRUE(isOk(appStatsResult));
+ ASSERT_EQ((uint64_t)1, appStatsResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, appStatsResult.value().rxBytes);
}
TEST_F(TrafficControllerTest, TestDeleteAllUidData) {
@@ -343,13 +354,12 @@
StatsKey tagStatsMapKey;
populateFakeStats(cookie, uid, tag, &tagStatsMapKey);
ASSERT_EQ(0, mTc.deleteTagData(0, TEST_UID));
- UidTag cookieMapkey;
- ASSERT_EQ(-1, findMapEntry(mFakeCookieTagMap, &cookie, &cookieMapkey));
- int counterSetResult;
- ASSERT_EQ(-1, findMapEntry(mFakeUidCounterSetMap, &uid, &counterSetResult));
- StatsValue statsMapResult;
- ASSERT_EQ(-1, findMapEntry(mFakeTagStatsMap, &tagStatsMapKey, &statsMapResult));
- ASSERT_EQ(-1, findMapEntry(mFakeUidStatsMap, &tagStatsMapKey, &statsMapResult));
+ ASSERT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie)));
+ ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid)));
+ ASSERT_FALSE(isOk(mFakeTagStatsMap.readValue(tagStatsMapKey)));
+ tagStatsMapKey.tag = 0;
+ ASSERT_FALSE(isOk(mFakeUidStatsMap.readValue(tagStatsMapKey)));
+ ASSERT_FALSE(isOk(mFakeAppUidStatsMap.readValue(TEST_UID)));
}
TEST_F(TrafficControllerTest, TestDeleteDataWithTwoTags) {
@@ -365,19 +375,19 @@
populateFakeStats(cookie1, uid, tag1, &tagStatsMapKey1);
populateFakeStats(cookie2, uid, tag2, &tagStatsMapKey2);
ASSERT_EQ(0, mTc.deleteTagData(TEST_TAG, TEST_UID));
- UidTag cookieMapResult;
- ASSERT_EQ(-1, findMapEntry(mFakeCookieTagMap, &cookie1, &cookieMapResult));
- ASSERT_EQ(0, findMapEntry(mFakeCookieTagMap, &cookie2, &cookieMapResult));
- ASSERT_EQ(TEST_UID, cookieMapResult.uid);
- ASSERT_EQ(TEST_TAG + 1, cookieMapResult.tag);
- int counterSetResult;
- ASSERT_EQ(0, findMapEntry(mFakeUidCounterSetMap, &uid, &counterSetResult));
- ASSERT_EQ(TEST_COUNTERSET, counterSetResult);
- StatsValue statsMapResult;
- ASSERT_EQ(-1, findMapEntry(mFakeTagStatsMap, &tagStatsMapKey1, &statsMapResult));
- ASSERT_EQ(0, findMapEntry(mFakeTagStatsMap, &tagStatsMapKey2, &statsMapResult));
- ASSERT_EQ((uint64_t)1, statsMapResult.rxPackets);
- ASSERT_EQ((uint64_t)100, statsMapResult.rxBytes);
+ ASSERT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie1)));
+ StatusOr<UidTag> cookieMapResult = mFakeCookieTagMap.readValue(cookie2);
+ ASSERT_TRUE(isOk(cookieMapResult));
+ ASSERT_EQ(TEST_UID, cookieMapResult.value().uid);
+ ASSERT_EQ(TEST_TAG + 1, cookieMapResult.value().tag);
+ StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid);
+ ASSERT_TRUE(isOk(counterSetResult));
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
+ ASSERT_FALSE(isOk(mFakeTagStatsMap.readValue(tagStatsMapKey1)));
+ StatusOr<StatsValue> statsMapResult = mFakeTagStatsMap.readValue(tagStatsMapKey2);
+ ASSERT_TRUE(isOk(statsMapResult));
+ ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
}
TEST_F(TrafficControllerTest, TestDeleteDataWithTwoUids) {
@@ -396,53 +406,56 @@
// Delete the stats of one of the uid. Check if it is properly collected by
// removedStats.
ASSERT_EQ(0, mTc.deleteTagData(0, uid2));
- UidTag cookieMapResult;
- ASSERT_EQ(-1, findMapEntry(mFakeCookieTagMap, &cookie2, &cookieMapResult));
- int counterSetResult;
- ASSERT_EQ(0, findMapEntry(mFakeUidCounterSetMap, &uid1, &counterSetResult));
- ASSERT_EQ(TEST_COUNTERSET, counterSetResult);
- ASSERT_EQ(-1, findMapEntry(mFakeUidCounterSetMap, &uid2, &counterSetResult));
- StatsValue statsMapResult;
- ASSERT_EQ(-1, findMapEntry(mFakeTagStatsMap, &tagStatsMapKey2, &statsMapResult));
+ ASSERT_FALSE(isOk(mFakeCookieTagMap.readValue(cookie2)));
+ StatusOr<uint8_t> counterSetResult = mFakeUidCounterSetMap.readValue(uid1);
+ ASSERT_TRUE(isOk(counterSetResult));
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
+ ASSERT_FALSE(isOk(mFakeUidCounterSetMap.readValue(uid2)));
+ ASSERT_FALSE(isOk(mFakeTagStatsMap.readValue(tagStatsMapKey2)));
tagStatsMapKey2.tag = 0;
- ASSERT_EQ(-1, findMapEntry(mFakeUidStatsMap, &tagStatsMapKey2, &statsMapResult));
+ ASSERT_FALSE(isOk(mFakeUidStatsMap.readValue(tagStatsMapKey2)));
+ ASSERT_FALSE(isOk(mFakeAppUidStatsMap.readValue(uid2)));
tagStatsMapKey1.tag = 0;
- ASSERT_EQ(0, findMapEntry(mFakeUidStatsMap, &tagStatsMapKey1, &statsMapResult));
- ASSERT_EQ((uint64_t)1, statsMapResult.rxPackets);
- ASSERT_EQ((uint64_t)100, statsMapResult.rxBytes);
+ StatusOr<StatsValue> statsMapResult = mFakeUidStatsMap.readValue(tagStatsMapKey1);
+ ASSERT_TRUE(isOk(statsMapResult));
+ ASSERT_EQ((uint64_t)1, statsMapResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, statsMapResult.value().rxBytes);
+ auto appStatsResult = mFakeAppUidStatsMap.readValue(uid1);
+ ASSERT_TRUE(isOk(appStatsResult));
+ ASSERT_EQ((uint64_t)1, appStatsResult.value().rxPackets);
+ ASSERT_EQ((uint64_t)100, appStatsResult.value().rxBytes);
// Delete the stats of the other uid.
ASSERT_EQ(0, mTc.deleteTagData(0, uid1));
- ASSERT_EQ(-1, findMapEntry(mFakeUidStatsMap, &tagStatsMapKey1, &statsMapResult));
+ ASSERT_FALSE(isOk(mFakeUidStatsMap.readValue(tagStatsMapKey1)));
+ ASSERT_FALSE(isOk(mFakeAppUidStatsMap.readValue(uid1)));
}
TEST_F(TrafficControllerTest, TestUpdateOwnerMapEntry) {
SKIP_IF_BPF_NOT_SUPPORTED;
uint32_t uid = TEST_UID;
- ASSERT_EQ(0, mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, DENY, BLACKLIST));
- uint8_t value;
- ASSERT_EQ(0, findMapEntry(mFakeDozableUidMap, &uid, &value));
- ASSERT_EQ((uint8_t)BPF_DROP, value);
+ ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, DENY, BLACKLIST)));
+ StatusOr<uint8_t> value = mFakeDozableUidMap.readValue(uid);
+ ASSERT_TRUE(isOk(value));
+ ASSERT_EQ(BPF_DROP, value.value());
uid = TEST_UID2;
- ASSERT_EQ(0, mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, ALLOW, WHITELIST));
- ASSERT_EQ(0, findMapEntry(mFakeDozableUidMap, &uid, &value));
- ASSERT_EQ((uint8_t)BPF_PASS, value);
+ ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, ALLOW, WHITELIST)));
+ value = mFakeDozableUidMap.readValue(uid);
+ ASSERT_TRUE(isOk(value));
+ ASSERT_EQ(BPF_PASS, value.value());
- ASSERT_EQ(0, mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, DENY, WHITELIST));
- ASSERT_EQ(-1, findMapEntry(mFakeDozableUidMap, &uid, &value));
- ASSERT_EQ(ENOENT, errno);
+ ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, DENY, WHITELIST)));
+ ASSERT_FALSE(isOk(mFakeDozableUidMap.readValue(uid)));
uid = TEST_UID;
- ASSERT_EQ(0, mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, ALLOW, BLACKLIST));
- ASSERT_EQ(-1, findMapEntry(mFakeDozableUidMap, &uid, &value));
- ASSERT_EQ(ENOENT, errno);
+ ASSERT_TRUE(isOk(mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, ALLOW, BLACKLIST)));
+ ASSERT_FALSE(isOk(mFakeDozableUidMap.readValue(uid)));
uid = TEST_UID3;
- ASSERT_EQ(-ENOENT, mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, ALLOW, BLACKLIST));
- ASSERT_EQ(-1, findMapEntry(mFakeDozableUidMap, &uid, &value));
- ASSERT_EQ(ENOENT, errno);
+ ASSERT_FALSE(isOk(mTc.updateOwnerMapEntry(mFakeDozableUidMap, uid, ALLOW, BLACKLIST)));
+ ASSERT_FALSE(isOk(mFakeDozableUidMap.readValue(uid)));
}
TEST_F(TrafficControllerTest, TestChangeUidOwnerRule) {
diff --git a/tests/Android.mk b/tests/Android.mk
index b0c503a..302ad1e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -24,7 +24,7 @@
LOCAL_CFLAGS += -Wno-varargs
EXTRA_LDLIBS := -lpthread
-LOCAL_SHARED_LIBRARIES += libbase libbinder libcrypto libcutils liblog \
+LOCAL_SHARED_LIBRARIES += libbase libbinder libbpf libcrypto libcutils liblog \
libnetd_client libnetutils libssl libutils
LOCAL_STATIC_LIBRARIES += libnetd_test_dnsresponder liblogwrap libnetdaidl_static \
libnetdutils libnetd_test_tun_interface libbpf
diff --git a/tests/bpf_base_test.cpp b/tests/bpf_base_test.cpp
index 41945e2..e023052 100644
--- a/tests/bpf_base_test.cpp
+++ b/tests/bpf_base_test.cpp
@@ -33,12 +33,14 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include "bpf/BpfMap.h"
#include "bpf/BpfUtils.h"
using namespace android::bpf;
using android::base::unique_fd;
using android::netdutils::status::ok;
+using android::netdutils::StatusOr;
namespace android {
namespace bpf {
@@ -83,57 +85,94 @@
TEST_F(BpfBasicTest, TestTagSocket) {
SKIP_IF_BPF_NOT_SUPPORTED;
- unique_fd cookieTagMap = unique_fd(mapRetrieve(COOKIE_TAG_MAP_PATH, 0));
- ASSERT_LE(0, cookieTagMap);
+ BpfMap<uint64_t, UidTag> cookieTagMap(mapRetrieve(COOKIE_TAG_MAP_PATH, 0));
+ ASSERT_LE(0, cookieTagMap.getMap());
int sock = socket(AF_INET6, SOCK_STREAM, 0);
ASSERT_LE(0, sock);
uint64_t cookie = getSocketCookie(sock);
ASSERT_NE(NONEXISTENT_COOKIE, cookie);
ASSERT_EQ(0, qtaguid_tagSocket(sock, TEST_TAG, TEST_UID));
- struct UidTag tagResult;
- ASSERT_EQ(0, findMapEntry(cookieTagMap, &cookie, &tagResult));
- ASSERT_EQ(TEST_UID, tagResult.uid);
- ASSERT_EQ(TEST_TAG, tagResult.tag);
+ StatusOr<UidTag> tagResult = cookieTagMap.readValue(cookie);
+ ASSERT_TRUE(isOk(tagResult));
+ ASSERT_EQ(TEST_UID, tagResult.value().uid);
+ ASSERT_EQ(TEST_TAG, tagResult.value().tag);
ASSERT_EQ(0, qtaguid_untagSocket(sock));
- ASSERT_EQ(-1, findMapEntry(cookieTagMap, &cookie, &tagResult));
- ASSERT_EQ(ENOENT, errno);
+ tagResult = cookieTagMap.readValue(cookie);
+ ASSERT_FALSE(isOk(tagResult));
+ ASSERT_EQ(ENOENT, tagResult.status().code());
+}
+
+TEST_F(BpfBasicTest, TestCloseSocketWithoutUntag) {
+ SKIP_IF_BPF_NOT_SUPPORTED;
+
+ BpfMap<uint64_t, UidTag> cookieTagMap(mapRetrieve(COOKIE_TAG_MAP_PATH, 0));
+ ASSERT_LE(0, cookieTagMap.getMap());
+ int sock = socket(AF_INET6, SOCK_STREAM, 0);
+ ASSERT_LE(0, sock);
+ uint64_t cookie = getSocketCookie(sock);
+ ASSERT_NE(NONEXISTENT_COOKIE, cookie);
+ ASSERT_EQ(0, qtaguid_tagSocket(sock, TEST_TAG, TEST_UID));
+ StatusOr<UidTag> tagResult = cookieTagMap.readValue(cookie);
+ ASSERT_TRUE(isOk(tagResult));
+ ASSERT_EQ(TEST_UID, tagResult.value().uid);
+ ASSERT_EQ(TEST_TAG, tagResult.value().tag);
+ ASSERT_EQ(0, close(sock));
+ // Check map periodically until sk destroy handler have done its job.
+ for (int i = 0; i < 10; i++) {
+ tagResult = cookieTagMap.readValue(cookie);
+ if (!isOk(tagResult)) {
+ ASSERT_EQ(ENOENT, tagResult.status().code());
+ return;
+ }
+ usleep(50);
+ }
+ FAIL() << "socket tag still exist after 500ms";
}
TEST_F(BpfBasicTest, TestChangeCounterSet) {
SKIP_IF_BPF_NOT_SUPPORTED;
- unique_fd uidCounterSetMap = unique_fd(mapRetrieve(UID_COUNTERSET_MAP_PATH, 0));
- ASSERT_LE(0, uidCounterSetMap);
+ BpfMap<uint32_t, uint8_t> uidCounterSetMap(mapRetrieve(UID_COUNTERSET_MAP_PATH, 0));
+ ASSERT_LE(0, uidCounterSetMap.getMap());
ASSERT_EQ(0, qtaguid_setCounterSet(TEST_COUNTERSET, TEST_UID));
uid_t uid = TEST_UID;
- int counterSetResult;
- ASSERT_EQ(0, findMapEntry(uidCounterSetMap, &uid, &counterSetResult));
- ASSERT_EQ(TEST_COUNTERSET, counterSetResult);
+ StatusOr<uint8_t> counterSetResult = uidCounterSetMap.readValue(uid);
+ ASSERT_TRUE(isOk(counterSetResult));
+ ASSERT_EQ(TEST_COUNTERSET, counterSetResult.value());
ASSERT_EQ(0, qtaguid_setCounterSet(DEFAULT_COUNTERSET, TEST_UID));
- ASSERT_EQ(-1, findMapEntry(uidCounterSetMap, &uid, &counterSetResult));
- ASSERT_EQ(ENOENT, errno);
+ counterSetResult = uidCounterSetMap.readValue(uid);
+ ASSERT_FALSE(isOk(counterSetResult));
+ ASSERT_EQ(ENOENT, counterSetResult.status().code());
}
TEST_F(BpfBasicTest, TestDeleteTagData) {
SKIP_IF_BPF_NOT_SUPPORTED;
- unique_fd uidStatsMap = unique_fd(mapRetrieve(UID_STATS_MAP_PATH, 0));
- ASSERT_LE(0, uidStatsMap);
- unique_fd tagStatsMap = unique_fd(mapRetrieve(TAG_STATS_MAP_PATH, 0));
- ASSERT_LE(0, tagStatsMap);
+ BpfMap<StatsKey, StatsValue> uidStatsMap(mapRetrieve(UID_STATS_MAP_PATH, 0));
+ ASSERT_LE(0, uidStatsMap.getMap());
+ BpfMap<StatsKey, StatsValue> tagStatsMap(mapRetrieve(TAG_STATS_MAP_PATH, 0));
+ ASSERT_LE(0, tagStatsMap.getMap());
+ BpfMap<uint32_t, StatsValue> appUidStatsMap(mapRetrieve(APP_UID_STATS_MAP_PATH, 0));
+ ASSERT_LE(0, appUidStatsMap.getMap());
StatsKey key = {.uid = TEST_UID, .tag = TEST_TAG, .counterSet = TEST_COUNTERSET,
.ifaceIndex = 1};
StatsValue statsMapValue = {.rxPackets = 1, .rxBytes = 100};
- EXPECT_EQ(0, writeToMapEntry(tagStatsMap, &key, &statsMapValue, BPF_ANY));
+ EXPECT_TRUE(isOk(tagStatsMap.writeValue(key, statsMapValue, BPF_ANY)));
key.tag = 0;
- EXPECT_EQ(0, writeToMapEntry(uidStatsMap, &key, &statsMapValue, BPF_ANY));
+ EXPECT_TRUE(isOk(uidStatsMap.writeValue(key, statsMapValue, BPF_ANY)));
+ EXPECT_TRUE(isOk(appUidStatsMap.writeValue(TEST_UID, statsMapValue, BPF_ANY)));
ASSERT_EQ(0, qtaguid_deleteTagData(0, TEST_UID));
- ASSERT_EQ(-1, findMapEntry(uidStatsMap, &key, &statsMapValue));
- ASSERT_EQ(ENOENT, errno);
+ StatusOr<StatsValue> statsResult = uidStatsMap.readValue(key);
+ ASSERT_FALSE(isOk(statsResult));
+ ASSERT_EQ(ENOENT, statsResult.status().code());
+ statsResult = appUidStatsMap.readValue(TEST_UID);
+ ASSERT_FALSE(isOk(statsResult));
+ ASSERT_EQ(ENOENT, statsResult.status().code());
key.tag = TEST_TAG;
- ASSERT_EQ(-1, findMapEntry(tagStatsMap, &key, &statsMapValue));
- ASSERT_EQ(ENOENT, errno);
+ statsResult = tagStatsMap.readValue(key);
+ ASSERT_FALSE(isOk(statsResult));
+ ASSERT_EQ(ENOENT, statsResult.status().code());
}
}