Use bpf maps to store permission information
In newer kernels, we can use cgroup socket filter to control inet socket
creation at run time instead of paranoid network kernel check. To
achieve that, we need to get the permission information from system
server when device boots or new packages are installed. This patch
provides a binder interface to do that and stores the information in a
bpf map. It also records the uids that have permission
UPDATE_DEVICE_STATS so netd no longer needs to query that from the
system server.
Bug: 111560570
Bug: 111560739
Test: netd_unit_test, netd_integration_test
Change-Id: I0c5919d85136feec44c4406ee0bd0028b131b942
diff --git a/server/TrafficController.cpp b/server/TrafficController.cpp
index 93d0101..9aaa9fa 100644
--- a/server/TrafficController.cpp
+++ b/server/TrafficController.cpp
@@ -71,27 +71,37 @@
constexpr int kSockDiagMsgType = SOCK_DIAG_BY_FAMILY;
constexpr int kSockDiagDoneMsgType = NLMSG_DONE;
-#define UID_MATCH_MSG_TRANS(result, target, match) \
- do { \
- if (match & target) { \
- result.append(StringPrintf(" %s", #target)); \
- match &= ~target; \
- } \
+#define FLAG_MSG_TRANS(result, flag, value) \
+ do { \
+ if (value & flag) { \
+ result.append(StringPrintf(" %s", #flag)); \
+ value &= ~flag; \
+ } \
} while (0)
const std::string uidMatchTypeToString(uint8_t match) {
std::string matchType;
- UID_MATCH_MSG_TRANS(matchType, HAPPY_BOX_MATCH, match);
- UID_MATCH_MSG_TRANS(matchType, PENALTY_BOX_MATCH, match);
- UID_MATCH_MSG_TRANS(matchType, DOZABLE_MATCH, match);
- UID_MATCH_MSG_TRANS(matchType, STANDBY_MATCH, match);
- UID_MATCH_MSG_TRANS(matchType, POWERSAVE_MATCH, match);
+ FLAG_MSG_TRANS(matchType, HAPPY_BOX_MATCH, match);
+ FLAG_MSG_TRANS(matchType, PENALTY_BOX_MATCH, match);
+ FLAG_MSG_TRANS(matchType, DOZABLE_MATCH, match);
+ FLAG_MSG_TRANS(matchType, STANDBY_MATCH, match);
+ FLAG_MSG_TRANS(matchType, POWERSAVE_MATCH, match);
if (match) {
return StringPrintf("Unknown match: %u", match);
}
return matchType;
}
+const std::string UidPermissionTypeToString(uint8_t permission) {
+ std::string permissionType;
+ FLAG_MSG_TRANS(permissionType, ALLOW_SOCK_CREATE, permission);
+ FLAG_MSG_TRANS(permissionType, ALLOW_UPDATE_DEVICE_STATS, permission);
+ if (permission) {
+ return StringPrintf("Unknown permission: %u", permission);
+ }
+ return permissionType;
+}
+
StatusOr<std::unique_ptr<NetlinkListenerInterface>> TrafficController::makeSkDestroyListener() {
const auto& sys = sSyscalls.get();
ASSIGN_OR_RETURN(auto event, sys.eventfd(0, EFD_CLOEXEC));
@@ -194,6 +204,8 @@
mUidOwnerMap.getOrCreate(UID_OWNER_MAP_SIZE, UID_OWNER_MAP_PATH, BPF_MAP_TYPE_HASH));
RETURN_IF_NOT_OK(changeOwnerAndMode(UID_OWNER_MAP_PATH, AID_ROOT, "UidOwnerMap", true));
RETURN_IF_NOT_OK(mUidOwnerMap.clear());
+ RETURN_IF_NOT_OK(mUidPermissionMap.getOrCreate(UID_OWNER_MAP_SIZE, UID_PERMISSION_MAP_PATH,
+ BPF_MAP_TYPE_HASH));
return netdutils::status::ok;
}
@@ -639,6 +651,33 @@
return ebpfSupported;
}
+void TrafficController::setPermissionForUids(int permission, const std::vector<uid_t>& uids) {
+ bool internet = (permission & INetd::PERMISSION_INTERNET);
+ bool privileged = (permission & INetd::PERMISSION_UPDATE_DEVICE_STATS);
+
+ for (uid_t uid : uids) {
+ if (internet) {
+ Status ret = mUidPermissionMap.writeValue(uid, ALLOW_SOCK_CREATE, BPF_ANY);
+ if (!isOk(ret)) {
+ ALOGE("Failed to grant INTERNET permission to uid: %u: %s", uid,
+ strerror(ret.code()));
+ }
+ } else {
+ Status ret = mUidPermissionMap.deleteValue(uid);
+ if (!isOk(ret) && ret.code() != ENOENT) {
+ ALOGE("Failed to revoke permission INTERNET from uid: %u: %s", uid,
+ strerror(ret.code()));
+ }
+ }
+
+ if (privileged) {
+ mPrivilegedUser.insert(uid);
+ } else {
+ mPrivilegedUser.erase(uid);
+ }
+ }
+}
+
std::string getProgramStatus(const char *path) {
int ret = access(path, R_OK);
if (ret == 0) {
@@ -850,6 +889,21 @@
if (!isOk(res)) {
dw.println("mUidOwnerMap print end with error: %s", res.msg().c_str());
}
+ dumpBpfMap("mUidPermissionMap", dw, "");
+ const auto printUidPermissionInfo = [&dw](const uint32_t& key, const uint8_t& value,
+ const BpfMap<uint32_t, uint8_t>&) {
+ dw.println("%u %s", key, UidPermissionTypeToString(value).c_str());
+ return netdutils::status::ok;
+ };
+ res = mUidPermissionMap.iterateWithValue(printUidPermissionInfo);
+ if (!isOk(res)) {
+ dw.println("mUidPermissionMap print end with error: %s", res.msg().c_str());
+ }
+
+ dumpBpfMap("mPrivilegedUser", dw, "");
+ for (uid_t uid : mPrivilegedUser) {
+ dw.println("%u ALLOW_UPDATE_DEVICE_STATS", (uint32_t)uid);
+ }
}
} // namespace net