1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "TrafficController"
18 #include <inttypes.h>
19 #include <linux/bpf.h>
20 #include <linux/if_ether.h>
21 #include <linux/in.h>
22 #include <linux/inet_diag.h>
23 #include <linux/netlink.h>
24 #include <linux/sock_diag.h>
25 #include <linux/unistd.h>
26 #include <net/if.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/socket.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <sys/utsname.h>
33 #include <sys/wait.h>
34 #include <mutex>
35 #include <unordered_set>
36 #include <vector>
37
38 #include <android-base/stringprintf.h>
39 #include <android-base/strings.h>
40 #include <android-base/unique_fd.h>
41 #include <netdutils/StatusOr.h>
42
43 #include <netdutils/Misc.h>
44 #include <netdutils/Syscalls.h>
45 #include <processgroup/processgroup.h>
46 #include "TrafficController.h"
47 #include "bpf/BpfMap.h"
48
49 #include "FirewallController.h"
50 #include "InterfaceController.h"
51 #include "NetlinkListener.h"
52 #include "netdutils/DumpWriter.h"
53 #include "qtaguid/qtaguid.h"
54
55 using namespace android::bpf; // NOLINT(google-build-using-namespace): grandfathered
56
57 namespace android {
58 namespace net {
59
60 using base::StringPrintf;
61 using base::unique_fd;
62 using netdutils::DumpWriter;
63 using netdutils::extract;
64 using netdutils::ScopedIndent;
65 using netdutils::Slice;
66 using netdutils::sSyscalls;
67 using netdutils::Status;
68 using netdutils::statusFromErrno;
69 using netdutils::StatusOr;
70 using netdutils::status::ok;
71
72 constexpr int kSockDiagMsgType = SOCK_DIAG_BY_FAMILY;
73 constexpr int kSockDiagDoneMsgType = NLMSG_DONE;
74 constexpr int PER_UID_STATS_ENTRIES_LIMIT = 500;
75 // At most 90% of the stats map may be used by tagged traffic entries. This ensures
76 // that 10% of the map is always available to count untagged traffic, one entry per UID.
77 // Otherwise, apps would be able to avoid data usage accounting entirely by filling up the
78 // map with tagged traffic entries.
79 constexpr int TOTAL_UID_STATS_ENTRIES_LIMIT = STATS_MAP_SIZE * 0.9;
80
81 static_assert(BPF_PERMISSION_INTERNET == INetd::PERMISSION_INTERNET,
82 "Mismatch between BPF and AIDL permissions: PERMISSION_INTERNET");
83 static_assert(BPF_PERMISSION_UPDATE_DEVICE_STATS == INetd::PERMISSION_UPDATE_DEVICE_STATS,
84 "Mismatch between BPF and AIDL permissions: PERMISSION_UPDATE_DEVICE_STATS");
85 static_assert(STATS_MAP_SIZE - TOTAL_UID_STATS_ENTRIES_LIMIT > 100,
86 "The limit for stats map is to high, stats data may be lost due to overflow");
87
88 #define FLAG_MSG_TRANS(result, flag, value) \
89 do { \
90 if ((value) & (flag)) { \
91 (result).append(" " #flag); \
92 (value) &= ~(flag); \
93 } \
94 } while (0)
95
uidMatchTypeToString(uint8_t match)96 const std::string uidMatchTypeToString(uint8_t match) {
97 std::string matchType;
98 FLAG_MSG_TRANS(matchType, HAPPY_BOX_MATCH, match);
99 FLAG_MSG_TRANS(matchType, PENALTY_BOX_MATCH, match);
100 FLAG_MSG_TRANS(matchType, DOZABLE_MATCH, match);
101 FLAG_MSG_TRANS(matchType, STANDBY_MATCH, match);
102 FLAG_MSG_TRANS(matchType, POWERSAVE_MATCH, match);
103 FLAG_MSG_TRANS(matchType, IIF_MATCH, match);
104 if (match) {
105 return StringPrintf("Unknown match: %u", match);
106 }
107 return matchType;
108 }
109
hasUpdateDeviceStatsPermission(uid_t uid)110 bool TrafficController::hasUpdateDeviceStatsPermission(uid_t uid) {
111 // This implementation is the same logic as method ActivityManager#checkComponentPermission.
112 // It implies that the calling uid can never be the same as PER_USER_RANGE.
113 uint32_t appId = uid % PER_USER_RANGE;
114 return ((appId == AID_ROOT) || (appId == AID_SYSTEM) ||
115 mPrivilegedUser.find(appId) != mPrivilegedUser.end());
116 }
117
UidPermissionTypeToString(int permission)118 const std::string UidPermissionTypeToString(int permission) {
119 if (permission == INetd::PERMISSION_NONE) {
120 return "PERMISSION_NONE";
121 }
122 if (permission == INetd::PERMISSION_UNINSTALLED) {
123 // This should never appear in the map, complain loudly if it does.
124 return "PERMISSION_UNINSTALLED error!";
125 }
126 std::string permissionType;
127 FLAG_MSG_TRANS(permissionType, BPF_PERMISSION_INTERNET, permission);
128 FLAG_MSG_TRANS(permissionType, BPF_PERMISSION_UPDATE_DEVICE_STATS, permission);
129 if (permission) {
130 return StringPrintf("Unknown permission: %u", permission);
131 }
132 return permissionType;
133 }
134
makeSkDestroyListener()135 StatusOr<std::unique_ptr<NetlinkListenerInterface>> TrafficController::makeSkDestroyListener() {
136 const auto& sys = sSyscalls.get();
137 ASSIGN_OR_RETURN(auto event, sys.eventfd(0, EFD_CLOEXEC));
138 const int domain = AF_NETLINK;
139 const int type = SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK;
140 const int protocol = NETLINK_INET_DIAG;
141 ASSIGN_OR_RETURN(auto sock, sys.socket(domain, type, protocol));
142
143 // TODO: if too many sockets are closed too quickly, we can overflow the socket buffer, and
144 // some entries in mCookieTagMap will not be freed. In order to fix this we would need to
145 // periodically dump all sockets and remove the tag entries for sockets that have been closed.
146 // For now, set a large-enough buffer that we can close hundreds of sockets without getting
147 // ENOBUFS and leaking mCookieTagMap entries.
148 int rcvbuf = 512 * 1024;
149 auto ret = sys.setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
150 if (!ret.ok()) {
151 ALOGW("Failed to set SkDestroyListener buffer size to %d: %s", rcvbuf, ret.msg().c_str());
152 }
153
154 sockaddr_nl addr = {
155 .nl_family = AF_NETLINK,
156 .nl_groups = 1 << (SKNLGRP_INET_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET_UDP_DESTROY - 1) |
157 1 << (SKNLGRP_INET6_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET6_UDP_DESTROY - 1)};
158 RETURN_IF_NOT_OK(sys.bind(sock, addr));
159
160 const sockaddr_nl kernel = {.nl_family = AF_NETLINK};
161 RETURN_IF_NOT_OK(sys.connect(sock, kernel));
162
163 std::unique_ptr<NetlinkListenerInterface> listener =
164 std::make_unique<NetlinkListener>(std::move(event), std::move(sock), "SkDestroyListen");
165
166 return listener;
167 }
168
TrafficController()169 TrafficController::TrafficController()
170 : mBpfEnabled(isBpfSupported()),
171 mPerUidStatsEntriesLimit(PER_UID_STATS_ENTRIES_LIMIT),
172 mTotalUidStatsEntriesLimit(TOTAL_UID_STATS_ENTRIES_LIMIT) {}
173
TrafficController(uint32_t perUidLimit,uint32_t totalLimit)174 TrafficController::TrafficController(uint32_t perUidLimit, uint32_t totalLimit)
175 : mBpfEnabled(isBpfSupported()),
176 mPerUidStatsEntriesLimit(perUidLimit),
177 mTotalUidStatsEntriesLimit(totalLimit) {}
178
initMaps()179 Status TrafficController::initMaps() {
180 std::lock_guard guard(mMutex);
181
182 RETURN_IF_NOT_OK(mCookieTagMap.init(COOKIE_TAG_MAP_PATH));
183 RETURN_IF_NOT_OK(mUidCounterSetMap.init(UID_COUNTERSET_MAP_PATH));
184 RETURN_IF_NOT_OK(mAppUidStatsMap.init(APP_UID_STATS_MAP_PATH));
185 RETURN_IF_NOT_OK(mStatsMapA.init(STATS_MAP_A_PATH));
186 RETURN_IF_NOT_OK(mStatsMapB.init(STATS_MAP_B_PATH));
187 RETURN_IF_NOT_OK(mIfaceIndexNameMap.init(IFACE_INDEX_NAME_MAP_PATH));
188 RETURN_IF_NOT_OK(mIfaceStatsMap.init(IFACE_STATS_MAP_PATH));
189
190 RETURN_IF_NOT_OK(mConfigurationMap.init(CONFIGURATION_MAP_PATH));
191 RETURN_IF_NOT_OK(
192 mConfigurationMap.writeValue(UID_RULES_CONFIGURATION_KEY, DEFAULT_CONFIG, BPF_ANY));
193 RETURN_IF_NOT_OK(mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, SELECT_MAP_A,
194 BPF_ANY));
195
196 RETURN_IF_NOT_OK(mUidOwnerMap.init(UID_OWNER_MAP_PATH));
197 RETURN_IF_NOT_OK(mUidOwnerMap.clear());
198 RETURN_IF_NOT_OK(mUidPermissionMap.init(UID_PERMISSION_MAP_PATH));
199
200 return netdutils::status::ok;
201 }
202
attachProgramToCgroup(const char * programPath,const unique_fd & cgroupFd,bpf_attach_type type)203 static Status attachProgramToCgroup(const char* programPath, const unique_fd& cgroupFd,
204 bpf_attach_type type) {
205 unique_fd cgroupProg(retrieveProgram(programPath));
206 if (cgroupProg == -1) {
207 int ret = errno;
208 ALOGE("Failed to get program from %s: %s", programPath, strerror(ret));
209 return statusFromErrno(ret, "cgroup program get failed");
210 }
211 if (android::bpf::attachProgram(type, cgroupProg, cgroupFd)) {
212 int ret = errno;
213 ALOGE("Program from %s attach failed: %s", programPath, strerror(ret));
214 return statusFromErrno(ret, "program attach failed");
215 }
216 return netdutils::status::ok;
217 }
218
initPrograms()219 static Status initPrograms() {
220 std::string cg2_path;
221
222 if (!CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cg2_path)) {
223 int ret = errno;
224 ALOGE("Failed to find cgroup v2 root");
225 return statusFromErrno(ret, "Failed to find cgroup v2 root");
226 }
227
228 unique_fd cg_fd(open(cg2_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
229 if (cg_fd == -1) {
230 int ret = errno;
231 ALOGE("Failed to open the cgroup directory: %s", strerror(ret));
232 return statusFromErrno(ret, "Open the cgroup directory failed");
233 }
234 RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_EGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_EGRESS));
235 RETURN_IF_NOT_OK(attachProgramToCgroup(BPF_INGRESS_PROG_PATH, cg_fd, BPF_CGROUP_INET_INGRESS));
236
237 // For the devices that support cgroup socket filter, the socket filter
238 // should be loaded successfully by bpfloader. So we attach the filter to
239 // cgroup if the program is pinned properly.
240 // TODO: delete the if statement once all devices should support cgroup
241 // socket filter (ie. the minimum kernel version required is 4.14).
242 if (!access(CGROUP_SOCKET_PROG_PATH, F_OK)) {
243 RETURN_IF_NOT_OK(
244 attachProgramToCgroup(CGROUP_SOCKET_PROG_PATH, cg_fd, BPF_CGROUP_INET_SOCK_CREATE));
245 }
246 return netdutils::status::ok;
247 }
248
start()249 Status TrafficController::start() {
250 if (!mBpfEnabled) {
251 return netdutils::status::ok;
252 }
253
254 /* When netd restarts from a crash without total system reboot, the program
255 * is still attached to the cgroup, detach it so the program can be freed
256 * and we can load and attach new program into the target cgroup.
257 *
258 * TODO: Scrape existing socket when run-time restart and clean up the map
259 * if the socket no longer exist
260 */
261
262 RETURN_IF_NOT_OK(initMaps());
263
264 RETURN_IF_NOT_OK(initPrograms());
265
266 // Fetch the list of currently-existing interfaces. At this point NetlinkHandler is
267 // already running, so it will call addInterface() when any new interface appears.
268 std::map<std::string, uint32_t> ifacePairs;
269 ASSIGN_OR_RETURN(ifacePairs, InterfaceController::getIfaceList());
270 for (const auto& ifacePair:ifacePairs) {
271 addInterface(ifacePair.first.c_str(), ifacePair.second);
272 }
273
274 auto result = makeSkDestroyListener();
275 if (!isOk(result)) {
276 ALOGE("Unable to create SkDestroyListener: %s", toString(result).c_str());
277 } else {
278 mSkDestroyListener = std::move(result.value());
279 }
280 // Rx handler extracts nfgenmsg looks up and invokes registered dispatch function.
281 const auto rxHandler = [this](const nlmsghdr&, const Slice msg) {
282 std::lock_guard guard(mMutex);
283 inet_diag_msg diagmsg = {};
284 if (extract(msg, diagmsg) < sizeof(inet_diag_msg)) {
285 ALOGE("Unrecognized netlink message: %s", toString(msg).c_str());
286 return;
287 }
288 uint64_t sock_cookie = static_cast<uint64_t>(diagmsg.id.idiag_cookie[0]) |
289 (static_cast<uint64_t>(diagmsg.id.idiag_cookie[1]) << 32);
290
291 Status s = mCookieTagMap.deleteValue(sock_cookie);
292 if (!isOk(s) && s.code() != ENOENT) {
293 ALOGE("Failed to delete cookie %" PRIx64 ": %s", sock_cookie, toString(s).c_str());
294 return;
295 }
296 };
297 expectOk(mSkDestroyListener->subscribe(kSockDiagMsgType, rxHandler));
298
299 // In case multiple netlink message comes in as a stream, we need to handle the rxDone message
300 // properly.
301 const auto rxDoneHandler = [](const nlmsghdr&, const Slice msg) {
302 // Ignore NLMSG_DONE messages
303 inet_diag_msg diagmsg = {};
304 extract(msg, diagmsg);
305 };
306 expectOk(mSkDestroyListener->subscribe(kSockDiagDoneMsgType, rxDoneHandler));
307
308 return netdutils::status::ok;
309 }
310
tagSocket(int sockFd,uint32_t tag,uid_t uid,uid_t callingUid)311 int TrafficController::tagSocket(int sockFd, uint32_t tag, uid_t uid, uid_t callingUid) {
312 std::lock_guard guard(mMutex);
313 if (uid != callingUid && !hasUpdateDeviceStatsPermission(callingUid)) {
314 return -EPERM;
315 }
316
317 if (!mBpfEnabled) {
318 if (legacy_tagSocket(sockFd, tag, uid)) return -errno;
319 return 0;
320 }
321
322 uint64_t sock_cookie = getSocketCookie(sockFd);
323 if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
324 UidTagValue newKey = {.uid = (uint32_t)uid, .tag = tag};
325
326 uint32_t totalEntryCount = 0;
327 uint32_t perUidEntryCount = 0;
328 // Now we go through the stats map and count how many entries are associated
329 // with target uid. If the uid entry hit the limit for each uid, we block
330 // the request to prevent the map from overflow. It is safe here to iterate
331 // over the map since when mMutex is hold, system server cannot toggle
332 // the live stats map and clean it. So nobody can delete entries from the map.
333 const auto countUidStatsEntries = [uid, &totalEntryCount, &perUidEntryCount](
334 const StatsKey& key,
335 const BpfMap<StatsKey, StatsValue>&) {
336 if (key.uid == uid) {
337 perUidEntryCount++;
338 }
339 totalEntryCount++;
340 return base::Result<void>();
341 };
342 auto configuration = mConfigurationMap.readValue(CURRENT_STATS_MAP_CONFIGURATION_KEY);
343 if (!configuration.ok()) {
344 ALOGE("Failed to get current configuration: %s, fd: %d",
345 strerror(configuration.error().code()), mConfigurationMap.getMap().get());
346 return -configuration.error().code();
347 }
348 if (configuration.value() != SELECT_MAP_A && configuration.value() != SELECT_MAP_B) {
349 ALOGE("unknown configuration value: %d", configuration.value());
350 return -EINVAL;
351 }
352
353 BpfMap<StatsKey, StatsValue>& currentMap =
354 (configuration.value() == SELECT_MAP_A) ? mStatsMapA : mStatsMapB;
355 base::Result<void> res = currentMap.iterate(countUidStatsEntries);
356 if (!res.ok()) {
357 ALOGE("Failed to count the stats entry in map %d: %s", currentMap.getMap().get(),
358 strerror(res.error().code()));
359 return -res.error().code();
360 }
361
362 if (totalEntryCount > mTotalUidStatsEntriesLimit ||
363 perUidEntryCount > mPerUidStatsEntriesLimit) {
364 ALOGE("Too many stats entries in the map, total count: %u, uid(%u) count: %u, blocking tag"
365 " request to prevent map overflow",
366 totalEntryCount, uid, perUidEntryCount);
367 return -EMFILE;
368 }
369 // Update the tag information of a socket to the cookieUidMap. Use BPF_ANY
370 // flag so it will insert a new entry to the map if that value doesn't exist
371 // yet. And update the tag if there is already a tag stored. Since the eBPF
372 // program in kernel only read this map, and is protected by rcu read lock. It
373 // should be fine to cocurrently update the map while eBPF program is running.
374 res = mCookieTagMap.writeValue(sock_cookie, newKey, BPF_ANY);
375 if (!res.ok()) {
376 ALOGE("Failed to tag the socket: %s, fd: %d", strerror(res.error().code()),
377 mCookieTagMap.getMap().get());
378 return -res.error().code();
379 }
380 return 0;
381 }
382
untagSocket(int sockFd)383 int TrafficController::untagSocket(int sockFd) {
384 std::lock_guard guard(mMutex);
385 if (!mBpfEnabled) {
386 if (legacy_untagSocket(sockFd)) return -errno;
387 return 0;
388 }
389 uint64_t sock_cookie = getSocketCookie(sockFd);
390
391 if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
392 base::Result<void> res = mCookieTagMap.deleteValue(sock_cookie);
393 if (!res.ok()) {
394 ALOGE("Failed to untag socket: %s\n", strerror(res.error().code()));
395 return -res.error().code();
396 }
397 return 0;
398 }
399
setCounterSet(int counterSetNum,uid_t uid,uid_t callingUid)400 int TrafficController::setCounterSet(int counterSetNum, uid_t uid, uid_t callingUid) {
401 if (counterSetNum < 0 || counterSetNum >= OVERFLOW_COUNTERSET) return -EINVAL;
402
403 std::lock_guard guard(mMutex);
404 if (!hasUpdateDeviceStatsPermission(callingUid)) return -EPERM;
405
406 if (!mBpfEnabled) {
407 if (legacy_setCounterSet(counterSetNum, uid)) return -errno;
408 return 0;
409 }
410
411 // The default counter set for all uid is 0, so deleting the current counterset for that uid
412 // will automatically set it to 0.
413 if (counterSetNum == 0) {
414 Status res = mUidCounterSetMap.deleteValue(uid);
415 if (isOk(res) || (!isOk(res) && res.code() == ENOENT)) {
416 return 0;
417 } else {
418 ALOGE("Failed to delete the counterSet: %s\n", strerror(res.code()));
419 return -res.code();
420 }
421 }
422 uint8_t tmpCounterSetNum = (uint8_t)counterSetNum;
423 Status res = mUidCounterSetMap.writeValue(uid, tmpCounterSetNum, BPF_ANY);
424 if (!isOk(res)) {
425 ALOGE("Failed to set the counterSet: %s, fd: %d", strerror(res.code()),
426 mUidCounterSetMap.getMap().get());
427 return -res.code();
428 }
429 return 0;
430 }
431
432 // This method only get called by system_server when an app get uinstalled, it
433 // is called inside removeUidsLocked() while holding mStatsLock. So it is safe
434 // to iterate and modify the stats maps.
deleteTagData(uint32_t tag,uid_t uid,uid_t callingUid)435 int TrafficController::deleteTagData(uint32_t tag, uid_t uid, uid_t callingUid) {
436 std::lock_guard guard(mMutex);
437 if (!hasUpdateDeviceStatsPermission(callingUid)) return -EPERM;
438
439 if (!mBpfEnabled) {
440 if (legacy_deleteTagData(tag, uid)) return -errno;
441 return 0;
442 }
443
444 // First we go through the cookieTagMap to delete the target uid tag combination. Or delete all
445 // the tags related to the uid if the tag is 0.
446 const auto deleteMatchedCookieEntries = [uid, tag](const uint64_t& key,
447 const UidTagValue& value,
448 BpfMap<uint64_t, UidTagValue>& map) {
449 if (value.uid == uid && (value.tag == tag || tag == 0)) {
450 auto res = map.deleteValue(key);
451 if (res.ok() || (res.error().code() == ENOENT)) {
452 return base::Result<void>();
453 }
454 ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", key,
455 strerror(res.error().code()));
456 }
457 // Move forward to next cookie in the map.
458 return base::Result<void>();
459 };
460 mCookieTagMap.iterateWithValue(deleteMatchedCookieEntries);
461 // Now we go through the Tag stats map and delete the data entry with correct uid and tag
462 // combination. Or all tag stats under that uid if the target tag is 0.
463 const auto deleteMatchedUidTagEntries = [uid, tag](const StatsKey& key,
464 BpfMap<StatsKey, StatsValue>& map) {
465 if (key.uid == uid && (key.tag == tag || tag == 0)) {
466 auto res = map.deleteValue(key);
467 if (res.ok() || (res.error().code() == ENOENT)) {
468 //Entry is deleted, use the current key to get a new nextKey;
469 return base::Result<void>();
470 }
471 ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", key.uid, key.tag,
472 strerror(res.error().code()));
473 }
474 return base::Result<void>();
475 };
476 mStatsMapB.iterate(deleteMatchedUidTagEntries);
477 mStatsMapA.iterate(deleteMatchedUidTagEntries);
478 // If the tag is not zero, we already deleted all the data entry required. If tag is 0, we also
479 // need to delete the stats stored in uidStatsMap and counterSet map.
480 if (tag != 0) return 0;
481
482 auto res = mUidCounterSetMap.deleteValue(uid);
483 if (!res.ok() && res.error().code() != ENOENT) {
484 ALOGE("Failed to delete counterSet data(uid=%u, tag=%u): %s\n", uid, tag,
485 strerror(res.error().code()));
486 }
487
488 auto deleteAppUidStatsEntry = [uid](const uint32_t& key,
489 BpfMap<uint32_t, StatsValue>& map) -> base::Result<void> {
490 if (key == uid) {
491 auto res = map.deleteValue(key);
492 if (res.ok() || (res.error().code() == ENOENT)) {
493 return {};
494 }
495 ALOGE("Failed to delete data(uid=%u): %s", key, strerror(res.error().code()));
496 }
497 return {};
498 };
499 mAppUidStatsMap.iterate(deleteAppUidStatsEntry);
500 return 0;
501 }
502
addInterface(const char * name,uint32_t ifaceIndex)503 int TrafficController::addInterface(const char* name, uint32_t ifaceIndex) {
504 if (!mBpfEnabled) return 0;
505
506 IfaceValue iface;
507 if (ifaceIndex == 0) {
508 ALOGE("Unknown interface %s(%d)", name, ifaceIndex);
509 return -1;
510 }
511
512 strlcpy(iface.name, name, sizeof(IfaceValue));
513 Status res = mIfaceIndexNameMap.writeValue(ifaceIndex, iface, BPF_ANY);
514 if (!isOk(res)) {
515 ALOGE("Failed to add iface %s(%d): %s", name, ifaceIndex, strerror(res.code()));
516 return -res.code();
517 }
518 return 0;
519 }
520
updateOwnerMapEntry(UidOwnerMatchType match,uid_t uid,FirewallRule rule,FirewallType type)521 Status TrafficController::updateOwnerMapEntry(UidOwnerMatchType match, uid_t uid, FirewallRule rule,
522 FirewallType type) {
523 std::lock_guard guard(mMutex);
524 if ((rule == ALLOW && type == ALLOWLIST) || (rule == DENY && type == DENYLIST)) {
525 RETURN_IF_NOT_OK(addRule(uid, match));
526 } else if ((rule == ALLOW && type == DENYLIST) || (rule == DENY && type == ALLOWLIST)) {
527 RETURN_IF_NOT_OK(removeRule(uid, match));
528 } else {
529 //Cannot happen.
530 return statusFromErrno(EINVAL, "");
531 }
532 return netdutils::status::ok;
533 }
534
jumpOpToMatch(BandwidthController::IptJumpOp jumpHandling)535 UidOwnerMatchType TrafficController::jumpOpToMatch(BandwidthController::IptJumpOp jumpHandling) {
536 switch (jumpHandling) {
537 case BandwidthController::IptJumpReject:
538 return PENALTY_BOX_MATCH;
539 case BandwidthController::IptJumpReturn:
540 return HAPPY_BOX_MATCH;
541 case BandwidthController::IptJumpNoAdd:
542 return NO_MATCH;
543 }
544 }
545
removeRule(uint32_t uid,UidOwnerMatchType match)546 Status TrafficController::removeRule(uint32_t uid, UidOwnerMatchType match) {
547 auto oldMatch = mUidOwnerMap.readValue(uid);
548 if (oldMatch.ok()) {
549 UidOwnerValue newMatch = {
550 .iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif,
551 .rule = static_cast<uint8_t>(oldMatch.value().rule & ~match),
552 };
553 if (newMatch.rule == 0) {
554 RETURN_IF_NOT_OK(mUidOwnerMap.deleteValue(uid));
555 } else {
556 RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY));
557 }
558 } else {
559 return statusFromErrno(ENOENT, StringPrintf("uid: %u does not exist in map", uid));
560 }
561 return netdutils::status::ok;
562 }
563
addRule(uint32_t uid,UidOwnerMatchType match,uint32_t iif)564 Status TrafficController::addRule(uint32_t uid, UidOwnerMatchType match, uint32_t iif) {
565 // iif should be non-zero if and only if match == MATCH_IIF
566 if (match == IIF_MATCH && iif == 0) {
567 return statusFromErrno(EINVAL, "Interface match must have nonzero interface index");
568 } else if (match != IIF_MATCH && iif != 0) {
569 return statusFromErrno(EINVAL, "Non-interface match must have zero interface index");
570 }
571 auto oldMatch = mUidOwnerMap.readValue(uid);
572 if (oldMatch.ok()) {
573 UidOwnerValue newMatch = {
574 .iif = iif ? iif : oldMatch.value().iif,
575 .rule = static_cast<uint8_t>(oldMatch.value().rule | match),
576 };
577 RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY));
578 } else {
579 UidOwnerValue newMatch = {
580 .iif = iif,
581 .rule = static_cast<uint8_t>(match),
582 };
583 RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY));
584 }
585 return netdutils::status::ok;
586 }
587
updateUidOwnerMap(const std::vector<std::string> & appStrUids,BandwidthController::IptJumpOp jumpHandling,BandwidthController::IptOp op)588 Status TrafficController::updateUidOwnerMap(const std::vector<std::string>& appStrUids,
589 BandwidthController::IptJumpOp jumpHandling,
590 BandwidthController::IptOp op) {
591 std::lock_guard guard(mMutex);
592 UidOwnerMatchType match = jumpOpToMatch(jumpHandling);
593 if (match == NO_MATCH) {
594 return statusFromErrno(
595 EINVAL, StringPrintf("invalid IptJumpOp: %d, command: %d", jumpHandling, match));
596 }
597 for (const auto& appStrUid : appStrUids) {
598 char* endPtr;
599 long uid = strtol(appStrUid.c_str(), &endPtr, 10);
600 if ((errno == ERANGE && (uid == LONG_MAX || uid == LONG_MIN)) ||
601 (endPtr == appStrUid.c_str()) || (*endPtr != '\0')) {
602 return statusFromErrno(errno, "invalid uid string:" + appStrUid);
603 }
604
605 if (op == BandwidthController::IptOpDelete) {
606 RETURN_IF_NOT_OK(removeRule(uid, match));
607 } else if (op == BandwidthController::IptOpInsert) {
608 RETURN_IF_NOT_OK(addRule(uid, match));
609 } else {
610 // Cannot happen.
611 return statusFromErrno(EINVAL, StringPrintf("invalid IptOp: %d, %d", op, match));
612 }
613 }
614 return netdutils::status::ok;
615 }
616
changeUidOwnerRule(ChildChain chain,uid_t uid,FirewallRule rule,FirewallType type)617 int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule,
618 FirewallType type) {
619 if (!mBpfEnabled) {
620 ALOGE("bpf is not set up, should use iptables rule");
621 return -ENOSYS;
622 }
623 Status res;
624 switch (chain) {
625 case DOZABLE:
626 res = updateOwnerMapEntry(DOZABLE_MATCH, uid, rule, type);
627 break;
628 case STANDBY:
629 res = updateOwnerMapEntry(STANDBY_MATCH, uid, rule, type);
630 break;
631 case POWERSAVE:
632 res = updateOwnerMapEntry(POWERSAVE_MATCH, uid, rule, type);
633 break;
634 case NONE:
635 default:
636 return -EINVAL;
637 }
638 if (!isOk(res)) {
639 ALOGE("change uid(%u) rule of %d failed: %s, rule: %d, type: %d", uid, chain,
640 res.msg().c_str(), rule, type);
641 return -res.code();
642 }
643 return 0;
644 }
645
replaceRulesInMap(const UidOwnerMatchType match,const std::vector<int32_t> & uids)646 Status TrafficController::replaceRulesInMap(const UidOwnerMatchType match,
647 const std::vector<int32_t>& uids) {
648 std::lock_guard guard(mMutex);
649 std::set<int32_t> uidSet(uids.begin(), uids.end());
650 std::vector<uint32_t> uidsToDelete;
651 auto getUidsToDelete = [&uidsToDelete, &uidSet](const uint32_t& key,
652 const BpfMap<uint32_t, UidOwnerValue>&) {
653 if (uidSet.find((int32_t) key) == uidSet.end()) {
654 uidsToDelete.push_back(key);
655 }
656 return base::Result<void>();
657 };
658 RETURN_IF_NOT_OK(mUidOwnerMap.iterate(getUidsToDelete));
659
660 for(auto uid : uidsToDelete) {
661 RETURN_IF_NOT_OK(removeRule(uid, match));
662 }
663
664 for (auto uid : uids) {
665 RETURN_IF_NOT_OK(addRule(uid, match));
666 }
667 return netdutils::status::ok;
668 }
669
addUidInterfaceRules(const int iif,const std::vector<int32_t> & uidsToAdd)670 Status TrafficController::addUidInterfaceRules(const int iif,
671 const std::vector<int32_t>& uidsToAdd) {
672 if (!mBpfEnabled) {
673 ALOGW("UID ingress interface filtering not possible without BPF owner match");
674 return statusFromErrno(EOPNOTSUPP, "eBPF not supported");
675 }
676 if (!iif) {
677 return statusFromErrno(EINVAL, "Interface rule must specify interface");
678 }
679 std::lock_guard guard(mMutex);
680
681 for (auto uid : uidsToAdd) {
682 netdutils::Status result = addRule(uid, IIF_MATCH, iif);
683 if (!isOk(result)) {
684 ALOGW("addRule failed(%d): uid=%d iif=%d", result.code(), uid, iif);
685 }
686 }
687 return netdutils::status::ok;
688 }
689
removeUidInterfaceRules(const std::vector<int32_t> & uidsToDelete)690 Status TrafficController::removeUidInterfaceRules(const std::vector<int32_t>& uidsToDelete) {
691 if (!mBpfEnabled) {
692 ALOGW("UID ingress interface filtering not possible without BPF owner match");
693 return statusFromErrno(EOPNOTSUPP, "eBPF not supported");
694 }
695 std::lock_guard guard(mMutex);
696
697 for (auto uid : uidsToDelete) {
698 netdutils::Status result = removeRule(uid, IIF_MATCH);
699 if (!isOk(result)) {
700 ALOGW("removeRule failed(%d): uid=%d", result.code(), uid);
701 }
702 }
703 return netdutils::status::ok;
704 }
705
replaceUidOwnerMap(const std::string & name,bool isAllowlist __unused,const std::vector<int32_t> & uids)706 int TrafficController::replaceUidOwnerMap(const std::string& name, bool isAllowlist __unused,
707 const std::vector<int32_t>& uids) {
708 // FirewallRule rule = isAllowlist ? ALLOW : DENY;
709 // FirewallType type = isAllowlist ? ALLOWLIST : DENYLIST;
710 Status res;
711 if (!name.compare(FirewallController::LOCAL_DOZABLE)) {
712 res = replaceRulesInMap(DOZABLE_MATCH, uids);
713 } else if (!name.compare(FirewallController::LOCAL_STANDBY)) {
714 res = replaceRulesInMap(STANDBY_MATCH, uids);
715 } else if (!name.compare(FirewallController::LOCAL_POWERSAVE)) {
716 res = replaceRulesInMap(POWERSAVE_MATCH, uids);
717 } else {
718 ALOGE("unknown chain name: %s", name.c_str());
719 return -EINVAL;
720 }
721 if (!isOk(res)) {
722 ALOGE("Failed to clean up chain: %s: %s", name.c_str(), res.msg().c_str());
723 return -res.code();
724 }
725 return 0;
726 }
727
toggleUidOwnerMap(ChildChain chain,bool enable)728 int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) {
729 std::lock_guard guard(mMutex);
730 uint32_t key = UID_RULES_CONFIGURATION_KEY;
731 auto oldConfiguration = mConfigurationMap.readValue(key);
732 if (!oldConfiguration.ok()) {
733 ALOGE("Cannot read the old configuration from map: %s",
734 oldConfiguration.error().message().c_str());
735 return -oldConfiguration.error().code();
736 }
737 Status res;
738 BpfConfig newConfiguration;
739 uint8_t match;
740 switch (chain) {
741 case DOZABLE:
742 match = DOZABLE_MATCH;
743 break;
744 case STANDBY:
745 match = STANDBY_MATCH;
746 break;
747 case POWERSAVE:
748 match = POWERSAVE_MATCH;
749 break;
750 default:
751 return -EINVAL;
752 }
753 newConfiguration =
754 enable ? (oldConfiguration.value() | match) : (oldConfiguration.value() & (~match));
755 res = mConfigurationMap.writeValue(key, newConfiguration, BPF_EXIST);
756 if (!isOk(res)) {
757 ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, res.msg().c_str());
758 }
759 return -res.code();
760 }
761
getBpfEnabled()762 bool TrafficController::getBpfEnabled() {
763 return mBpfEnabled;
764 }
765
swapActiveStatsMap()766 Status TrafficController::swapActiveStatsMap() {
767 std::lock_guard guard(mMutex);
768
769 if (!mBpfEnabled) {
770 return statusFromErrno(EOPNOTSUPP, "This device doesn't have eBPF support");
771 }
772
773 uint32_t key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
774 auto oldConfiguration = mConfigurationMap.readValue(key);
775 if (!oldConfiguration.ok()) {
776 ALOGE("Cannot read the old configuration from map: %s",
777 oldConfiguration.error().message().c_str());
778 return Status(oldConfiguration.error().code(), oldConfiguration.error().message());
779 }
780
781 // Write to the configuration map to inform the kernel eBPF program to switch
782 // from using one map to the other. Use flag BPF_EXIST here since the map should
783 // be already populated in initMaps.
784 uint8_t newConfigure = (oldConfiguration.value() == SELECT_MAP_A) ? SELECT_MAP_B : SELECT_MAP_A;
785 auto res = mConfigurationMap.writeValue(CURRENT_STATS_MAP_CONFIGURATION_KEY, newConfigure,
786 BPF_EXIST);
787 if (!res.ok()) {
788 ALOGE("Failed to toggle the stats map: %s", strerror(res.error().code()));
789 return res;
790 }
791 // After changing the config, we need to make sure all the current running
792 // eBPF programs are finished and all the CPUs are aware of this config change
793 // before we modify the old map. So we do a special hack here to wait for
794 // the kernel to do a synchronize_rcu(). Once the kernel called
795 // synchronize_rcu(), the config we just updated will be available to all cores
796 // and the next eBPF programs triggered inside the kernel will use the new
797 // map configuration. So once this function returns we can safely modify the
798 // old stats map without concerning about race between the kernel and
799 // userspace.
800 int ret = synchronizeKernelRCU();
801 if (ret) {
802 ALOGE("map swap synchronize_rcu() ended with failure: %s", strerror(-ret));
803 return statusFromErrno(-ret, "map swap synchronize_rcu() failed");
804 }
805 return netdutils::status::ok;
806 }
807
setPermissionForUids(int permission,const std::vector<uid_t> & uids)808 void TrafficController::setPermissionForUids(int permission, const std::vector<uid_t>& uids) {
809 std::lock_guard guard(mMutex);
810 if (permission == INetd::PERMISSION_UNINSTALLED) {
811 for (uid_t uid : uids) {
812 // Clean up all permission information for the related uid if all the
813 // packages related to it are uninstalled.
814 mPrivilegedUser.erase(uid);
815 if (mBpfEnabled) {
816 Status ret = mUidPermissionMap.deleteValue(uid);
817 if (!isOk(ret) && ret.code() != ENOENT) {
818 ALOGE("Failed to clean up the permission for %u: %s", uid,
819 strerror(ret.code()));
820 }
821 }
822 }
823 return;
824 }
825
826 bool privileged = (permission & INetd::PERMISSION_UPDATE_DEVICE_STATS);
827
828 for (uid_t uid : uids) {
829 if (privileged) {
830 mPrivilegedUser.insert(uid);
831 } else {
832 mPrivilegedUser.erase(uid);
833 }
834
835 // Skip the bpf map operation if not supported.
836 if (!mBpfEnabled) {
837 continue;
838 }
839 // The map stores all the permissions that the UID has, except if the only permission
840 // the UID has is the INTERNET permission, then the UID should not appear in the map.
841 if (permission != INetd::PERMISSION_INTERNET) {
842 Status ret = mUidPermissionMap.writeValue(uid, permission, BPF_ANY);
843 if (!isOk(ret)) {
844 ALOGE("Failed to set permission: %s of uid(%u) to permission map: %s",
845 UidPermissionTypeToString(permission).c_str(), uid, strerror(ret.code()));
846 }
847 } else {
848 Status ret = mUidPermissionMap.deleteValue(uid);
849 if (!isOk(ret) && ret.code() != ENOENT) {
850 ALOGE("Failed to remove uid %u from permission map: %s", uid, strerror(ret.code()));
851 }
852 }
853 }
854 }
855
getProgramStatus(const char * path)856 std::string getProgramStatus(const char *path) {
857 int ret = access(path, R_OK);
858 if (ret == 0) {
859 return StringPrintf("OK");
860 }
861 if (ret != 0 && errno == ENOENT) {
862 return StringPrintf("program is missing at: %s", path);
863 }
864 return StringPrintf("check Program %s error: %s", path, strerror(errno));
865 }
866
getMapStatus(const base::unique_fd & map_fd,const char * path)867 std::string getMapStatus(const base::unique_fd& map_fd, const char* path) {
868 if (map_fd.get() < 0) {
869 return StringPrintf("map fd lost");
870 }
871 if (access(path, F_OK) != 0) {
872 return StringPrintf("map not pinned to location: %s", path);
873 }
874 return StringPrintf("OK");
875 }
876
877 // NOLINTNEXTLINE(google-runtime-references): grandfathered pass by non-const reference
dumpBpfMap(const std::string & mapName,DumpWriter & dw,const std::string & header)878 void dumpBpfMap(const std::string& mapName, DumpWriter& dw, const std::string& header) {
879 dw.blankline();
880 dw.println("%s:", mapName.c_str());
881 if (!header.empty()) {
882 dw.println(header);
883 }
884 }
885
886 const String16 TrafficController::DUMP_KEYWORD = String16("trafficcontroller");
887
dump(DumpWriter & dw,bool verbose)888 void TrafficController::dump(DumpWriter& dw, bool verbose) {
889 std::lock_guard guard(mMutex);
890 ScopedIndent indentTop(dw);
891 dw.println("TrafficController");
892
893 ScopedIndent indentPreBpfModule(dw);
894 dw.println("BPF module status: %s", mBpfEnabled ? "enabled" : "disabled");
895 dw.println("BPF support level: %s", BpfLevelToString(getBpfSupportLevel()).c_str());
896
897 if (!mBpfEnabled) {
898 return;
899 }
900
901 dw.blankline();
902 dw.println("mCookieTagMap status: %s",
903 getMapStatus(mCookieTagMap.getMap(), COOKIE_TAG_MAP_PATH).c_str());
904 dw.println("mUidCounterSetMap status: %s",
905 getMapStatus(mUidCounterSetMap.getMap(), UID_COUNTERSET_MAP_PATH).c_str());
906 dw.println("mAppUidStatsMap status: %s",
907 getMapStatus(mAppUidStatsMap.getMap(), APP_UID_STATS_MAP_PATH).c_str());
908 dw.println("mStatsMapA status: %s",
909 getMapStatus(mStatsMapA.getMap(), STATS_MAP_A_PATH).c_str());
910 dw.println("mStatsMapB status: %s",
911 getMapStatus(mStatsMapB.getMap(), STATS_MAP_B_PATH).c_str());
912 dw.println("mIfaceIndexNameMap status: %s",
913 getMapStatus(mIfaceIndexNameMap.getMap(), IFACE_INDEX_NAME_MAP_PATH).c_str());
914 dw.println("mIfaceStatsMap status: %s",
915 getMapStatus(mIfaceStatsMap.getMap(), IFACE_STATS_MAP_PATH).c_str());
916 dw.println("mConfigurationMap status: %s",
917 getMapStatus(mConfigurationMap.getMap(), CONFIGURATION_MAP_PATH).c_str());
918 dw.println("mUidOwnerMap status: %s",
919 getMapStatus(mUidOwnerMap.getMap(), UID_OWNER_MAP_PATH).c_str());
920
921 dw.blankline();
922 dw.println("Cgroup ingress program status: %s",
923 getProgramStatus(BPF_INGRESS_PROG_PATH).c_str());
924 dw.println("Cgroup egress program status: %s", getProgramStatus(BPF_EGRESS_PROG_PATH).c_str());
925 dw.println("xt_bpf ingress program status: %s",
926 getProgramStatus(XT_BPF_INGRESS_PROG_PATH).c_str());
927 dw.println("xt_bpf egress program status: %s",
928 getProgramStatus(XT_BPF_EGRESS_PROG_PATH).c_str());
929 dw.println("xt_bpf bandwidth allowlist program status: %s",
930 getProgramStatus(XT_BPF_ALLOWLIST_PROG_PATH).c_str());
931 dw.println("xt_bpf bandwidth denylist program status: %s",
932 getProgramStatus(XT_BPF_DENYLIST_PROG_PATH).c_str());
933
934 if (!verbose) {
935 return;
936 }
937
938 dw.blankline();
939 dw.println("BPF map content:");
940
941 ScopedIndent indentForMapContent(dw);
942
943 // Print CookieTagMap content.
944 dumpBpfMap("mCookieTagMap", dw, "");
945 const auto printCookieTagInfo = [&dw](const uint64_t& key, const UidTagValue& value,
946 const BpfMap<uint64_t, UidTagValue>&) {
947 dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", key, value.tag, value.uid);
948 return base::Result<void>();
949 };
950 base::Result<void> res = mCookieTagMap.iterateWithValue(printCookieTagInfo);
951 if (!res.ok()) {
952 dw.println("mCookieTagMap print end with error: %s", res.error().message().c_str());
953 }
954
955 // Print UidCounterSetMap Content
956 dumpBpfMap("mUidCounterSetMap", dw, "");
957 const auto printUidInfo = [&dw](const uint32_t& key, const uint8_t& value,
958 const BpfMap<uint32_t, uint8_t>&) {
959 dw.println("%u %u", key, value);
960 return base::Result<void>();
961 };
962 res = mUidCounterSetMap.iterateWithValue(printUidInfo);
963 if (!res.ok()) {
964 dw.println("mUidCounterSetMap print end with error: %s", res.error().message().c_str());
965 }
966
967 // Print AppUidStatsMap content
968 std::string appUidStatsHeader = StringPrintf("uid rxBytes rxPackets txBytes txPackets");
969 dumpBpfMap("mAppUidStatsMap:", dw, appUidStatsHeader);
970 auto printAppUidStatsInfo = [&dw](const uint32_t& key, const StatsValue& value,
971 const BpfMap<uint32_t, StatsValue>&) {
972 dw.println("%u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, value.rxBytes,
973 value.rxPackets, value.txBytes, value.txPackets);
974 return base::Result<void>();
975 };
976 res = mAppUidStatsMap.iterateWithValue(printAppUidStatsInfo);
977 if (!res.ok()) {
978 dw.println("mAppUidStatsMap print end with error: %s", res.error().message().c_str());
979 }
980
981 // Print uidStatsMap content
982 std::string statsHeader = StringPrintf("ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes"
983 " rxPackets txBytes txPackets");
984 dumpBpfMap("mStatsMapA", dw, statsHeader);
985 const auto printStatsInfo = [&dw, this](const StatsKey& key, const StatsValue& value,
986 const BpfMap<StatsKey, StatsValue>&) {
987 uint32_t ifIndex = key.ifaceIndex;
988 auto ifname = mIfaceIndexNameMap.readValue(ifIndex);
989 if (!ifname.ok()) {
990 ifname = IfaceValue{"unknown"};
991 }
992 dw.println("%u %s 0x%x %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifIndex,
993 ifname.value().name, key.tag, key.uid, key.counterSet, value.rxBytes,
994 value.rxPackets, value.txBytes, value.txPackets);
995 return base::Result<void>();
996 };
997 res = mStatsMapA.iterateWithValue(printStatsInfo);
998 if (!res.ok()) {
999 dw.println("mStatsMapA print end with error: %s", res.error().message().c_str());
1000 }
1001
1002 // Print TagStatsMap content.
1003 dumpBpfMap("mStatsMapB", dw, statsHeader);
1004 res = mStatsMapB.iterateWithValue(printStatsInfo);
1005 if (!res.ok()) {
1006 dw.println("mStatsMapB print end with error: %s", res.error().message().c_str());
1007 }
1008
1009 // Print ifaceIndexToNameMap content.
1010 dumpBpfMap("mIfaceIndexNameMap", dw, "");
1011 const auto printIfaceNameInfo = [&dw](const uint32_t& key, const IfaceValue& value,
1012 const BpfMap<uint32_t, IfaceValue>&) {
1013 const char* ifname = value.name;
1014 dw.println("ifaceIndex=%u ifaceName=%s", key, ifname);
1015 return base::Result<void>();
1016 };
1017 res = mIfaceIndexNameMap.iterateWithValue(printIfaceNameInfo);
1018 if (!res.ok()) {
1019 dw.println("mIfaceIndexNameMap print end with error: %s", res.error().message().c_str());
1020 }
1021
1022 // Print ifaceStatsMap content
1023 std::string ifaceStatsHeader = StringPrintf("ifaceIndex ifaceName rxBytes rxPackets txBytes"
1024 " txPackets");
1025 dumpBpfMap("mIfaceStatsMap:", dw, ifaceStatsHeader);
1026 const auto printIfaceStatsInfo = [&dw, this](const uint32_t& key, const StatsValue& value,
1027 const BpfMap<uint32_t, StatsValue>&) {
1028 auto ifname = mIfaceIndexNameMap.readValue(key);
1029 if (!ifname.ok()) {
1030 ifname = IfaceValue{"unknown"};
1031 }
1032 dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, key, ifname.value().name,
1033 value.rxBytes, value.rxPackets, value.txBytes, value.txPackets);
1034 return base::Result<void>();
1035 };
1036 res = mIfaceStatsMap.iterateWithValue(printIfaceStatsInfo);
1037 if (!res.ok()) {
1038 dw.println("mIfaceStatsMap print end with error: %s", res.error().message().c_str());
1039 }
1040
1041 dw.blankline();
1042
1043 uint32_t key = UID_RULES_CONFIGURATION_KEY;
1044 auto configuration = mConfigurationMap.readValue(key);
1045 if (configuration.ok()) {
1046 dw.println("current ownerMatch configuration: %d%s", configuration.value(),
1047 uidMatchTypeToString(configuration.value()).c_str());
1048 } else {
1049 dw.println("mConfigurationMap read ownerMatch configure failed with error: %s",
1050 configuration.error().message().c_str());
1051 }
1052
1053 key = CURRENT_STATS_MAP_CONFIGURATION_KEY;
1054 configuration = mConfigurationMap.readValue(key);
1055 if (configuration.ok()) {
1056 const char* statsMapDescription = "???";
1057 switch (configuration.value()) {
1058 case SELECT_MAP_A:
1059 statsMapDescription = "SELECT_MAP_A";
1060 break;
1061 case SELECT_MAP_B:
1062 statsMapDescription = "SELECT_MAP_B";
1063 break;
1064 // No default clause, so if we ever add a third map, this code will fail to build.
1065 }
1066 dw.println("current statsMap configuration: %d %s", configuration.value(),
1067 statsMapDescription);
1068 } else {
1069 dw.println("mConfigurationMap read stats map configure failed with error: %s",
1070 configuration.error().message().c_str());
1071 }
1072 dumpBpfMap("mUidOwnerMap", dw, "");
1073 const auto printUidMatchInfo = [&dw, this](const uint32_t& key, const UidOwnerValue& value,
1074 const BpfMap<uint32_t, UidOwnerValue>&) {
1075 if (value.rule & IIF_MATCH) {
1076 auto ifname = mIfaceIndexNameMap.readValue(value.iif);
1077 if (ifname.ok()) {
1078 dw.println("%u %s %s", key, uidMatchTypeToString(value.rule).c_str(),
1079 ifname.value().name);
1080 } else {
1081 dw.println("%u %s %u", key, uidMatchTypeToString(value.rule).c_str(), value.iif);
1082 }
1083 } else {
1084 dw.println("%u %s", key, uidMatchTypeToString(value.rule).c_str());
1085 }
1086 return base::Result<void>();
1087 };
1088 res = mUidOwnerMap.iterateWithValue(printUidMatchInfo);
1089 if (!res.ok()) {
1090 dw.println("mUidOwnerMap print end with error: %s", res.error().message().c_str());
1091 }
1092 dumpBpfMap("mUidPermissionMap", dw, "");
1093 const auto printUidPermissionInfo = [&dw](const uint32_t& key, const int& value,
1094 const BpfMap<uint32_t, uint8_t>&) {
1095 dw.println("%u %s", key, UidPermissionTypeToString(value).c_str());
1096 return base::Result<void>();
1097 };
1098 res = mUidPermissionMap.iterateWithValue(printUidPermissionInfo);
1099 if (!res.ok()) {
1100 dw.println("mUidPermissionMap print end with error: %s", res.error().message().c_str());
1101 }
1102
1103 dumpBpfMap("mPrivilegedUser", dw, "");
1104 for (uid_t uid : mPrivilegedUser) {
1105 dw.println("%u ALLOW_UPDATE_DEVICE_STATS", (uint32_t)uid);
1106 }
1107 }
1108
1109 } // namespace net
1110 } // namespace android
1111