Use xt_bpf programs to do bandwidth control
To completely move away from xt_qtaguid module, the bandwidth controller
should not using it for uid owner match any more. Instead, it can use a
eBPF map to store the uid need to be matched and use two eBPF program
running on the xt_bpf hooks to filter out the packet.
Bug: 80649292
Test: ./netd_unit_test
Change-Id: I8e9c7cb3371aae0c24ccc6f64e05e6cbd4f78aae
diff --git a/server/TrafficController.cpp b/server/TrafficController.cpp
index d6a6480..18c3689 100644
--- a/server/TrafficController.cpp
+++ b/server/TrafficController.cpp
@@ -45,7 +45,6 @@
#include <netdutils/Syscalls.h>
#include "TrafficController.h"
#include "bpf/BpfMap.h"
-#include "bpf/bpf_shared.h"
#include "DumpWriter.h"
#include "FirewallController.h"
@@ -117,19 +116,7 @@
}
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);
+ map.clear();
uint32_t mapSettingKey = UID_MAP_ENABLED;
uint8_t defaultMapState = 0;
return map.writeValue(mapSettingKey, defaultMapState, BPF_NOEXIST);
@@ -187,6 +174,11 @@
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_IF_NOT_OK(mBandwidthUidMap.getOrCreate(UID_OWNER_MAP_SIZE, BANDWIDTH_UID_MAP_PATH,
+ BPF_MAP_TYPE_HASH));
+ RETURN_IF_NOT_OK(changeOwnerAndMode(BANDWIDTH_UID_MAP_PATH, AID_ROOT, "BandwidthUidMap", true));
+ mBandwidthUidMap.clear();
return netdutils::status::ok;
}
@@ -265,6 +257,14 @@
if (ret != 0 && errno == ENOENT) {
prog_args.push_back((char*)"-m");
}
+ ret = access(XT_BPF_WHITELIST_PROG_PATH, R_OK);
+ if (ret != 0 && errno == ENOENT) {
+ prog_args.push_back((char*)"-w");
+ }
+ ret = access(XT_BPF_BLACKLIST_PROG_PATH, R_OK);
+ if (ret != 0 && errno == ENOENT) {
+ prog_args.push_back((char*)"-b");
+ }
if (prog_args.size() == 1) {
// all program are loaded already.
@@ -447,6 +447,59 @@
return netdutils::status::ok;
}
+BandwithMatchType TrafficController::jumpOpToMatch(BandwidthController::IptJumpOp jumpHandling) {
+ switch (jumpHandling) {
+ case BandwidthController::IptJumpReject:
+ return BLACKLISTMATCH;
+ case BandwidthController::IptJumpReturn:
+ return WHITELISTMATCH;
+ case BandwidthController::IptJumpNoAdd:
+ return NO_MATCH;
+ }
+}
+
+Status TrafficController::updateBandwidthUidMap(const std::vector<std::string>& appStrUids,
+ BandwidthController::IptJumpOp jumpHandling,
+ BandwidthController::IptOp op) {
+ uint8_t command = jumpOpToMatch(jumpHandling);
+ if (command == NO_MATCH) {
+ return statusFromErrno(EINVAL, StringPrintf("invalid IptJumpOp: %d, command: %d",
+ jumpHandling, command));
+ }
+ for (const auto& appStrUid : appStrUids) {
+ char* endPtr;
+ long uid = strtol(appStrUid.c_str(), &endPtr, 10);
+ if ((errno == ERANGE && (uid == LONG_MAX || uid == LONG_MIN)) ||
+ (endPtr == appStrUid.c_str()) || (*endPtr != '\0')) {
+ return statusFromErrno(errno, "invalid uid string:" + appStrUid);
+ }
+
+ auto match = mBandwidthUidMap.readValue(uid);
+ if (op == BandwidthController::IptOpDelete) {
+ if(!isOk(match)) {
+ return statusFromErrno(EINVAL, StringPrintf("uid(%ld): %s does not exist in map",
+ uid, appStrUid.c_str()));
+ }
+ uint8_t newValue = match.value() & ~command;
+ if (newValue == 0) {
+ RETURN_IF_NOT_OK(mBandwidthUidMap.deleteValue((uint32_t)uid));
+ } else {
+ RETURN_IF_NOT_OK(mBandwidthUidMap.writeValue((uint32_t)uid, newValue, BPF_ANY));
+ }
+ } else if (op == BandwidthController::IptOpInsert) {
+ uint8_t newValue = command;
+ if (isOk(match)) {
+ newValue |= match.value();
+ }
+ RETURN_IF_NOT_OK(mBandwidthUidMap.writeValue((uint32_t)uid, newValue, BPF_ANY));
+ } else {
+ // Cannot happen.
+ return statusFromErrno(EINVAL, StringPrintf("invalid IptOp: %d, %d", op, command));
+ }
+ }
+ return netdutils::status::ok;
+}
+
int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule,
FirewallType type) {
std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
@@ -622,6 +675,8 @@
getMapStatus(mStandbyUidMap.getMap(), STANDBY_UID_MAP_PATH).c_str());
dw.println("mPowerSaveUidMap status: %s",
getMapStatus(mPowerSaveUidMap.getMap(), POWERSAVE_UID_MAP_PATH).c_str());
+ dw.println("mBandwidthUidMap status: %s",
+ getMapStatus(mBandwidthUidMap.getMap(), BANDWIDTH_UID_MAP_PATH).c_str());
dw.blankline();
dw.println("Cgroup ingress program status: %s",
@@ -631,6 +686,10 @@
getProgramStatus(XT_BPF_INGRESS_PROG_PATH).c_str());
dw.println("xt_bpf egress program status: %s",
getProgramStatus(XT_BPF_EGRESS_PROG_PATH).c_str());
+ dw.println("xt_bpf bandwidth whitelist program status: %s",
+ getProgramStatus(XT_BPF_WHITELIST_PROG_PATH).c_str());
+ dw.println("xt_bpf bandwidth blacklist program status: %s",
+ getProgramStatus(XT_BPF_BLACKLIST_PROG_PATH).c_str());
if(!verbose) return;
@@ -756,6 +815,11 @@
dw.println("mDozableUidMap print end with error: %s", res.msg().c_str());
}
+ dumpBpfMap("mBandwidthUidMap", dw, "");
+ res = mBandwidthUidMap.iterateWithValue(printUidInfo);
+ if (!isOk(res)) {
+ dw.println("mBandwidthUidMap print end with error: %s", res.msg().c_str());
+ }
dw.decIndent();
dw.decIndent();