blob: b101174a61e88c2b5b6490589d8636d2b7f2e5d2 [file] [log] [blame]
Chenbo Fengf2759682017-10-10 17:31:57 -07001/*
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
Chenbo Fengc10a8a42017-12-15 13:56:33 -080017#define LOG_TAG "TrafficController"
Chenbo Feng33cc1032017-10-23 15:16:37 -070018#include <inttypes.h>
Chenbo Fengf2759682017-10-10 17:31:57 -070019#include <linux/bpf.h>
20#include <linux/if_ether.h>
21#include <linux/in.h>
22#include <linux/inet_diag.h>
Chenbo Feng116d0552017-12-04 17:25:19 -080023#include <linux/netlink.h>
24#include <linux/sock_diag.h>
Chenbo Fengf2759682017-10-10 17:31:57 -070025#include <linux/unistd.h>
Chenbo Fengc10a8a42017-12-15 13:56:33 -080026#include <net/if.h>
Chenbo Fengf2759682017-10-10 17:31:57 -070027#include <stdlib.h>
28#include <string.h>
29#include <sys/socket.h>
Chenbo Fengc10a8a42017-12-15 13:56:33 -080030#include <sys/stat.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080031#include <sys/types.h>
Chenbo Feng33cc1032017-10-23 15:16:37 -070032#include <sys/utsname.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080033#include <sys/wait.h>
Chenbo Feng89c12f12018-03-21 10:29:18 -070034#include <mutex>
Chenbo Fengc10a8a42017-12-15 13:56:33 -080035#include <unordered_set>
36#include <vector>
37
38#include <android-base/stringprintf.h>
Chenbo Fengef297172018-03-26 10:53:33 -070039#include <android-base/strings.h>
Chenbo Fengc10a8a42017-12-15 13:56:33 -080040#include <android-base/unique_fd.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080041#include <logwrap/logwrap.h>
Chenbo Fengc10a8a42017-12-15 13:56:33 -080042#include <netdutils/StatusOr.h>
Chenbo Fengf2759682017-10-10 17:31:57 -070043
Chenbo Feng116d0552017-12-04 17:25:19 -080044#include <netdutils/Misc.h>
45#include <netdutils/Syscalls.h>
Chenbo Fengf2759682017-10-10 17:31:57 -070046#include "TrafficController.h"
Chenbo Fengc10a8a42017-12-15 13:56:33 -080047#include "bpf/BpfUtils.h"
Chenbo Feng89c12f12018-03-21 10:29:18 -070048#include "bpf/bpf_shared.h"
Chenbo Feng116d0552017-12-04 17:25:19 -080049
Chenbo Fengef297172018-03-26 10:53:33 -070050#include "DumpWriter.h"
Chenbo Feng89c12f12018-03-21 10:29:18 -070051#include "FirewallController.h"
Chenbo Feng7e974052018-02-28 22:57:21 -080052#include "InterfaceController.h"
Chenbo Feng116d0552017-12-04 17:25:19 -080053#include "NetlinkListener.h"
Chenbo Feng33cc1032017-10-23 15:16:37 -070054#include "qtaguid/qtaguid.h"
Chenbo Fengf2759682017-10-10 17:31:57 -070055
Chenbo Fengc10a8a42017-12-15 13:56:33 -080056using namespace android::bpf;
Chenbo Fengf2759682017-10-10 17:31:57 -070057
58namespace android {
59namespace net {
60
Chenbo Fengc10a8a42017-12-15 13:56:33 -080061using base::StringPrintf;
62using base::unique_fd;
Chenbo Fengef297172018-03-26 10:53:33 -070063using base::Join;
Chenbo Feng116d0552017-12-04 17:25:19 -080064using netdutils::extract;
65using netdutils::Slice;
66using netdutils::sSyscalls;
Chenbo Fengc10a8a42017-12-15 13:56:33 -080067using netdutils::Status;
68using netdutils::statusFromErrno;
69using netdutils::StatusOr;
Chenbo Feng116d0552017-12-04 17:25:19 -080070using netdutils::status::ok;
Chenbo Fengc10a8a42017-12-15 13:56:33 -080071
Chenbo Feng116d0552017-12-04 17:25:19 -080072constexpr int kSockDiagMsgType = SOCK_DIAG_BY_FAMILY;
73constexpr int kSockDiagDoneMsgType = NLMSG_DONE;
74
75StatusOr<std::unique_ptr<NetlinkListenerInterface>> makeSkDestroyListener() {
76 const auto& sys = sSyscalls.get();
77 ASSIGN_OR_RETURN(auto event, sys.eventfd(0, EFD_CLOEXEC));
78 const int domain = AF_NETLINK;
79 const int type = SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK;
80 const int protocol = NETLINK_INET_DIAG;
81 ASSIGN_OR_RETURN(auto sock, sys.socket(domain, type, protocol));
82
83 sockaddr_nl addr = {
84 .nl_family = AF_NETLINK,
85 .nl_groups = 1 << (SKNLGRP_INET_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET_UDP_DESTROY - 1) |
86 1 << (SKNLGRP_INET6_TCP_DESTROY - 1) | 1 << (SKNLGRP_INET6_UDP_DESTROY - 1)};
87 RETURN_IF_NOT_OK(sys.bind(sock, addr));
88
89 const sockaddr_nl kernel = {.nl_family = AF_NETLINK};
90 RETURN_IF_NOT_OK(sys.connect(sock, kernel));
91
92 std::unique_ptr<NetlinkListenerInterface> listener =
93 std::make_unique<NetlinkListener>(std::move(event), std::move(sock));
94
95 return listener;
96}
97
Chenbo Feng89c12f12018-03-21 10:29:18 -070098Status changeOwnerAndMode(const char* path, gid_t group, const char* debugName, bool netdOnly) {
99 int ret = chown(path, AID_ROOT, group);
100 if (ret != 0) return statusFromErrno(errno, StringPrintf("change %s group failed", debugName));
101
102 if (netdOnly) {
103 ret = chmod(path, S_IRWXU);
104 } else {
105 // Allow both netd and system server to obtain map fd from the path.
106 // chmod doesn't grant permission to all processes in that group to
107 // read/write the bpf map. They still need correct sepolicy to
108 // read/write the map.
109 ret = chmod(path, S_IRWXU | S_IRGRP);
110 }
111 if (ret != 0) return statusFromErrno(errno, StringPrintf("change %s mode failed", debugName));
112 return netdutils::status::ok;
113}
114
115TrafficController::TrafficController() {
Chenbo Fengf43bf812017-12-15 18:27:22 -0800116 ebpfSupported = hasBpfSupport();
Chenbo Feng89c12f12018-03-21 10:29:18 -0700117}
118
119Status initialOwnerMap(const unique_fd& map_fd, const std::string mapName) {
120 uint32_t mapSettingKey = UID_MAP_ENABLED;
121 uint32_t defaultMapState = 0;
122 int ret = writeToMapEntry(map_fd, &mapSettingKey, &defaultMapState, BPF_NOEXIST);
123 // If it is already exist, it might be a runtime restart and we just keep
124 // the old state.
125 if (ret && errno != EEXIST) {
126 return statusFromErrno(errno, "Fail to set the initial state of " + mapName);
127 }
128 return netdutils::status::ok;
129}
130
131Status TrafficController::initMaps() {
132 std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
133 ASSIGN_OR_RETURN(mCookieTagMap,
134 setUpBPFMap(sizeof(uint64_t), sizeof(struct UidTag), COOKIE_UID_MAP_SIZE,
135 COOKIE_TAG_MAP_PATH, BPF_MAP_TYPE_HASH));
136
137 RETURN_IF_NOT_OK(changeOwnerAndMode(COOKIE_TAG_MAP_PATH, AID_NET_BW_ACCT, "CookieTagMap",
138 false));
139
140 ASSIGN_OR_RETURN(mUidCounterSetMap,
141 setUpBPFMap(sizeof(uint32_t), sizeof(uint32_t), UID_COUNTERSET_MAP_SIZE,
142 UID_COUNTERSET_MAP_PATH, BPF_MAP_TYPE_HASH));
143 RETURN_IF_NOT_OK(changeOwnerAndMode(UID_COUNTERSET_MAP_PATH, AID_NET_BW_ACCT,
144 "UidCounterSetMap", false));
145
146 ASSIGN_OR_RETURN(mUidStatsMap,
147 setUpBPFMap(sizeof(struct StatsKey), sizeof(struct StatsValue),
148 UID_STATS_MAP_SIZE, UID_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
149 RETURN_IF_NOT_OK(changeOwnerAndMode(UID_STATS_MAP_PATH, AID_NET_BW_STATS, "UidStatsMap",
150 false));
151
152 ASSIGN_OR_RETURN(mTagStatsMap,
153 setUpBPFMap(sizeof(struct StatsKey), sizeof(struct StatsValue),
154 TAG_STATS_MAP_SIZE, TAG_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
155 RETURN_IF_NOT_OK(changeOwnerAndMode(TAG_STATS_MAP_PATH, AID_NET_BW_STATS, "TagStatsMap",
156 false));
157
158 ASSIGN_OR_RETURN(mIfaceIndexNameMap,
159 setUpBPFMap(sizeof(uint32_t), IFNAMSIZ, IFACE_INDEX_NAME_MAP_SIZE,
160 IFACE_INDEX_NAME_MAP_PATH, BPF_MAP_TYPE_HASH));
161 RETURN_IF_NOT_OK(changeOwnerAndMode(IFACE_INDEX_NAME_MAP_PATH, AID_NET_BW_STATS,
162 "IfaceIndexNameMap", false));
163
164 ASSIGN_OR_RETURN(mDozableUidMap,
165 setUpBPFMap(sizeof(uint32_t), sizeof(uint8_t), UID_OWNER_MAP_SIZE,
166 DOZABLE_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
167 RETURN_IF_NOT_OK(changeOwnerAndMode(DOZABLE_UID_MAP_PATH, AID_ROOT, "DozableUidMap", true));
168 RETURN_IF_NOT_OK(initialOwnerMap(mDozableUidMap, "DozableUidMap"));
169
170 ASSIGN_OR_RETURN(mStandbyUidMap,
171 setUpBPFMap(sizeof(uint32_t), sizeof(uint8_t), UID_OWNER_MAP_SIZE,
172 STANDBY_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
173 RETURN_IF_NOT_OK(changeOwnerAndMode(STANDBY_UID_MAP_PATH, AID_ROOT, "StandbyUidMap", true));
174 RETURN_IF_NOT_OK(initialOwnerMap(mStandbyUidMap, "StandbyUidMap"));
175
176 ASSIGN_OR_RETURN(mPowerSaveUidMap,
177 setUpBPFMap(sizeof(uint32_t), sizeof(uint8_t), UID_OWNER_MAP_SIZE,
178 POWERSAVE_UID_MAP_PATH, BPF_MAP_TYPE_HASH));
179 RETURN_IF_NOT_OK(changeOwnerAndMode(POWERSAVE_UID_MAP_PATH, AID_ROOT, "PowerSaveUidMap", true));
180 RETURN_IF_NOT_OK(initialOwnerMap(mPowerSaveUidMap, "PowerSaveUidMap"));
181
182 ASSIGN_OR_RETURN(mIfaceStatsMap,
183 setUpBPFMap(sizeof(uint32_t), sizeof(struct StatsValue), IFACE_STATS_MAP_SIZE,
184 IFACE_STATS_MAP_PATH, BPF_MAP_TYPE_HASH));
185 RETURN_IF_NOT_OK(changeOwnerAndMode(IFACE_STATS_MAP_PATH, AID_NET_BW_STATS, "IfaceStatsMap",
186 false));
187 return netdutils::status::ok;
188}
189
190Status TrafficController::start() {
191
Chenbo Fengf43bf812017-12-15 18:27:22 -0800192 if (!ebpfSupported) {
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800193 return netdutils::status::ok;
Chenbo Fengf2759682017-10-10 17:31:57 -0700194 }
195
196 /* When netd restart from a crash without total system reboot, the program
197 * is still attached to the cgroup, detach it so the program can be freed
198 * and we can load and attach new program into the target cgroup.
199 *
200 * TODO: Scrape existing socket when run-time restart and clean up the map
201 * if the socket no longer exist
202 */
203
Chenbo Feng89c12f12018-03-21 10:29:18 -0700204 RETURN_IF_NOT_OK(initMaps());
Chenbo Feng7e974052018-02-28 22:57:21 -0800205
206 // Fetch the list of currently-existing interfaces. At this point NetlinkHandler is
207 // already running, so it will call addInterface() when any new interface appears.
208 std::map<std::string, uint32_t> ifacePairs;
209 ASSIGN_OR_RETURN(ifacePairs, InterfaceController::getIfaceList());
210 for (const auto& ifacePair:ifacePairs) {
211 addInterface(ifacePair.first.c_str(), ifacePair.second);
212 }
213
Chenbo Feng5ed17992018-03-13 21:30:49 -0700214
Chenbo Feng116d0552017-12-04 17:25:19 -0800215 auto result = makeSkDestroyListener();
216 if (!isOk(result)) {
217 ALOGE("Unable to create SkDestroyListener: %s", toString(result).c_str());
218 } else {
219 mSkDestroyListener = std::move(result.value());
220 }
221 // Rx handler extracts nfgenmsg looks up and invokes registered dispatch function.
222 const auto rxHandler = [this](const nlmsghdr&, const Slice msg) {
223 inet_diag_msg diagmsg = {};
224 if (extract(msg, diagmsg) < sizeof(inet_diag_msg)) {
225 ALOGE("unrecognized netlink message: %s", toString(msg).c_str());
226 return;
227 }
228 uint64_t sock_cookie = static_cast<uint64_t>(diagmsg.id.idiag_cookie[0]) |
229 (static_cast<uint64_t>(diagmsg.id.idiag_cookie[1]) << 32);
230
231 deleteMapEntry(mCookieTagMap, &sock_cookie);
232 };
233 expectOk(mSkDestroyListener->subscribe(kSockDiagMsgType, rxHandler));
234
235 // In case multiple netlink message comes in as a stream, we need to handle the rxDone message
236 // properly.
237 const auto rxDoneHandler = [](const nlmsghdr&, const Slice msg) {
238 // Ignore NLMSG_DONE messages
239 inet_diag_msg diagmsg = {};
240 extract(msg, diagmsg);
241 };
242 expectOk(mSkDestroyListener->subscribe(kSockDiagDoneMsgType, rxDoneHandler));
243
Chenbo Feng05393d82018-01-09 15:18:43 -0800244 int* status = nullptr;
245
246 std::vector<const char*> prog_args{
247 "/system/bin/bpfloader",
248 };
Chenbo Feng89c12f12018-03-21 10:29:18 -0700249 int ret = access(BPF_INGRESS_PROG_PATH, R_OK);
Chenbo Feng05393d82018-01-09 15:18:43 -0800250 if (ret != 0 && errno == ENOENT) {
251 prog_args.push_back((char*)"-i");
252 }
253 ret = access(BPF_EGRESS_PROG_PATH, R_OK);
254 if (ret != 0 && errno == ENOENT) {
255 prog_args.push_back((char*)"-e");
256 }
Chenbo Feng5ed17992018-03-13 21:30:49 -0700257 ret = access(XT_BPF_INGRESS_PROG_PATH, R_OK);
258 if (ret != 0 && errno == ENOENT) {
259 prog_args.push_back((char*)"-p");
260 }
261 ret = access(XT_BPF_EGRESS_PROG_PATH, R_OK);
262 if (ret != 0 && errno == ENOENT) {
263 prog_args.push_back((char*)"-m");
264 }
Chenbo Feng05393d82018-01-09 15:18:43 -0800265
266 if (prog_args.size() == 1) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700267 // all program are loaded already.
Chenbo Feng05393d82018-01-09 15:18:43 -0800268 return netdutils::status::ok;
269 }
270
271 prog_args.push_back(nullptr);
272 ret = android_fork_execvp(prog_args.size(), (char**)prog_args.data(), status, false, true);
273 if (ret) {
274 ret = errno;
275 ALOGE("failed to execute %s: %s", prog_args[0], strerror(errno));
276 return statusFromErrno(ret, "run bpf loader failed");
277 }
278 return netdutils::status::ok;
Chenbo Fengf2759682017-10-10 17:31:57 -0700279}
280
Chenbo Fengf2759682017-10-10 17:31:57 -0700281int TrafficController::tagSocket(int sockFd, uint32_t tag, uid_t uid) {
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800282 if (legacy_tagSocket(sockFd, tag, uid)) return -errno;
283 if (!ebpfSupported) return 0;
Chenbo Feng33cc1032017-10-23 15:16:37 -0700284
Chenbo Fengf2759682017-10-10 17:31:57 -0700285 uint64_t sock_cookie = getSocketCookie(sockFd);
286 if (sock_cookie == INET_DIAG_NOCOOKIE) return -errno;
287 UidTag newKey = {.uid = (uint32_t)uid, .tag = tag};
288
289 // Update the tag information of a socket to the cookieUidMap. Use BPF_ANY
290 // flag so it will insert a new entry to the map if that value doesn't exist
291 // yet. And update the tag if there is already a tag stored. Since the eBPF
292 // program in kernel only read this map, and is protected by rcu read lock. It
293 // should be fine to cocurrently update the map while eBPF program is running.
294 int res = writeToMapEntry(mCookieTagMap, &sock_cookie, &newKey, BPF_ANY);
295 if (res < 0) {
296 res = -errno;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800297 ALOGE("Failed to tag the socket: %s, fd: %d", strerror(errno), mCookieTagMap.get());
Chenbo Fengf2759682017-10-10 17:31:57 -0700298 }
299
300 return res;
301}
302
303int TrafficController::untagSocket(int sockFd) {
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800304 if (legacy_untagSocket(sockFd)) return -errno;
Chenbo Feng33cc1032017-10-23 15:16:37 -0700305 if (!ebpfSupported) return 0;
Chenbo Fengf2759682017-10-10 17:31:57 -0700306 uint64_t sock_cookie = getSocketCookie(sockFd);
307
308 if (sock_cookie == INET_DIAG_NOCOOKIE) return -errno;
309 int res = deleteMapEntry(mCookieTagMap, &sock_cookie);
310 if (res) {
311 res = -errno;
312 ALOGE("Failed to untag socket: %s\n", strerror(errno));
313 }
314 return res;
315}
316
317int TrafficController::setCounterSet(int counterSetNum, uid_t uid) {
318 if (counterSetNum < 0 || counterSetNum >= COUNTERSETS_LIMIT) return -EINVAL;
Chenbo Fengf2759682017-10-10 17:31:57 -0700319 int res;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800320 if (legacy_setCounterSet(counterSetNum, uid)) return -errno;
321 if (!ebpfSupported) return 0;
322
323 // The default counter set for all uid is 0, so deleting the current counterset for that uid
324 // will automatically set it to 0.
Chenbo Fengf2759682017-10-10 17:31:57 -0700325 if (counterSetNum == 0) {
326 res = deleteMapEntry(mUidCounterSetMap, &uid);
327 if (res == 0 || (res == -1 && errno == ENOENT)) {
328 return 0;
329 } else {
330 ALOGE("Failed to delete the counterSet: %s\n", strerror(errno));
331 return -errno;
332 }
333 }
334
335 res = writeToMapEntry(mUidCounterSetMap, &uid, &counterSetNum, BPF_ANY);
336 if (res < 0) {
337 res = -errno;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800338 ALOGE("Failed to set the counterSet: %s, fd: %d", strerror(errno), mUidCounterSetMap.get());
Chenbo Fengf2759682017-10-10 17:31:57 -0700339 }
340 return res;
341}
342
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800343// TODO: Add a lock for delete tag Data so when several request for different uid comes in, they do
344// not race with each other.
Chenbo Fengf2759682017-10-10 17:31:57 -0700345int TrafficController::deleteTagData(uint32_t tag, uid_t uid) {
346 int res = 0;
Chenbo Feng33cc1032017-10-23 15:16:37 -0700347
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800348 if (legacy_deleteTagData(tag, uid)) return -errno;
Chenbo Feng33cc1032017-10-23 15:16:37 -0700349 if (!ebpfSupported) return 0;
350
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800351 uint64_t curCookie = NONEXIST_COOKIE;
Chenbo Fengf2759682017-10-10 17:31:57 -0700352 uint64_t nextCookie = 0;
353 UidTag tmp_uidtag;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800354 std::vector<uint64_t> cookieList;
355 // First we go through the cookieTagMap to delete the target uid tag combination. Or delete all
356 // the tags related to the uid if the tag is 0, we start the map iteration with a cookie of
357 // INET_DIAG_NOCOOKIE because it's guaranteed that that will not be in the map.
358 while (getNextMapKey(mCookieTagMap, &curCookie, &nextCookie) != -1) {
359 res = findMapEntry(mCookieTagMap, &nextCookie, &tmp_uidtag);
Chenbo Fengf2759682017-10-10 17:31:57 -0700360 if (res < 0) {
361 res = -errno;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800362 ALOGE("Failed to get tag info(cookie = %" PRIu64 ": %s\n", nextCookie, strerror(errno));
363 // Continue to look for next entry.
364 curCookie = nextCookie;
365 continue;
Chenbo Fengf2759682017-10-10 17:31:57 -0700366 }
367
368 if (tmp_uidtag.uid == uid && (tmp_uidtag.tag == tag || tag == 0)) {
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800369 res = deleteMapEntry(mCookieTagMap, &nextCookie);
370 if (res < 0 && errno != ENOENT) {
Chenbo Fengf2759682017-10-10 17:31:57 -0700371 res = -errno;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800372 ALOGE("Failed to delete data(cookie = %" PRIu64 "): %s\n", nextCookie,
373 strerror(errno));
Chenbo Fengf2759682017-10-10 17:31:57 -0700374 }
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800375 } else {
376 // Move forward to next cookie in the map.
Chenbo Fengf2759682017-10-10 17:31:57 -0700377 curCookie = nextCookie;
378 }
379 }
380
381 // Now we go through the Tag stats map and delete the data entry with correct uid and tag
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800382 // combination. Or all tag stats under that uid if the target tag is 0. The initial key is
383 // set to the nonexist_statskey because it will never be in the map, and thus getNextMapKey will
384 // return 0 and set nextKey to the first key in the map.
Chenbo Fengf2759682017-10-10 17:31:57 -0700385 struct StatsKey curKey, nextKey;
Chenbo Fengf43bf812017-12-15 18:27:22 -0800386 curKey = android::bpf::NONEXISTENT_STATSKEY;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800387 while (getNextMapKey(mTagStatsMap, &curKey, &nextKey) != -1) {
388 if (nextKey.uid == uid && (nextKey.tag == tag || tag == 0)) {
389 res = deleteMapEntry(mTagStatsMap, &nextKey);
390 if (res < 0 && errno != ENOENT) {
391 // Skip the current entry if unexpected error happened.
392 ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", nextKey.uid, nextKey.tag,
393 strerror(errno));
394 curKey = nextKey;
Chenbo Fengf2759682017-10-10 17:31:57 -0700395 }
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800396 } else {
Chenbo Fengf2759682017-10-10 17:31:57 -0700397 curKey = nextKey;
398 }
399 }
400
401 // If the tag is not zero, we already deleted all the data entry required. If tag is 0, we also
Chenbo Fenged37fea2017-12-13 19:35:01 -0800402 // need to delete the stats stored in uidStatsMap and counterSet map.
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800403 if (tag != 0) return 0;
404
405 res = deleteMapEntry(mUidCounterSetMap, &uid);
406 if (res < 0 && errno != ENOENT) {
407 ALOGE("Failed to delete counterSet data(uid=%u, tag=%u): %s\n", uid, tag, strerror(errno));
408 }
409
410 // For the uid stats deleted from the map, move them into a special
411 // removed uid entry. The removed uid is stored in uid 0, tag 0 and
412 // counterSet as COUNTERSETS_LIMIT.
413 StatsKey removedStatsKey = {0, 0, COUNTERSETS_LIMIT, 0};
Chenbo Fengf43bf812017-12-15 18:27:22 -0800414 StatsValue removedStatsTotal = {};
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800415 res = findMapEntry(mUidStatsMap, &removedStatsKey, &removedStatsTotal);
416 if (res < 0 && errno != ENOENT) {
417 ALOGE("Failed to get stats of removed uid: %s", strerror(errno));
418 }
419
Chenbo Fengf43bf812017-12-15 18:27:22 -0800420 curKey = android::bpf::NONEXISTENT_STATSKEY;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800421 while (getNextMapKey(mUidStatsMap, &curKey, &nextKey) != -1) {
422 if (nextKey.uid == uid) {
Chenbo Fengf43bf812017-12-15 18:27:22 -0800423 StatsValue old_stats = {};
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800424 res = findMapEntry(mUidStatsMap, &nextKey, &old_stats);
425 if (res < 0) {
426 if (errno != ENOENT) {
427 // if errno is ENOENT Somebody else deleted nextKey. Lookup the next key from
428 // curKey. If we have other error. Skip this key to avoid an infinite loop.
429 curKey = nextKey;
430 }
431 continue;
Chenbo Fengf2759682017-10-10 17:31:57 -0700432 }
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800433 res = deleteMapEntry(mUidStatsMap, &nextKey);
434 if (res < 0 && errno != ENOENT) {
435 ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", nextKey.uid, nextKey.tag,
436 strerror(errno));
437 curKey = nextKey;
438 continue;
439 }
Chenbo Fengeac6c472018-02-05 15:06:23 -0800440 removedStatsTotal.rxPackets += old_stats.rxPackets;
441 removedStatsTotal.rxBytes += old_stats.rxBytes;
442 removedStatsTotal.txPackets += old_stats.txPackets;
443 removedStatsTotal.txBytes += old_stats.txBytes;
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800444 } else {
Chenbo Fengf2759682017-10-10 17:31:57 -0700445 curKey = nextKey;
446 }
447 }
Chenbo Fengc10a8a42017-12-15 13:56:33 -0800448
449 res = writeToMapEntry(mUidStatsMap, &removedStatsKey, &removedStatsTotal, BPF_ANY);
450 if (res) {
451 res = -errno;
452 ALOGE("Failed to add deleting stats to removed uid: %s", strerror(errno));
453 }
Chenbo Fengf2759682017-10-10 17:31:57 -0700454 return res;
455}
456
Chenbo Feng7e974052018-02-28 22:57:21 -0800457int TrafficController::addInterface(const char* name, uint32_t ifaceIndex) {
458 int res = 0;
459 if (!ebpfSupported) return res;
460
461 char ifaceName[IFNAMSIZ];
462 if (ifaceIndex == 0) {
463 ALOGE("Unknow interface %s(%d)", name, ifaceIndex);
464 return -1;
465 }
466
467 strlcpy(ifaceName, name, sizeof(ifaceName));
468 res = writeToMapEntry(mIfaceIndexNameMap, &ifaceIndex, ifaceName, BPF_ANY);
469 if (res) {
470 res = -errno;
471 ALOGE("Failed to add iface %s(%d): %s", name, ifaceIndex, strerror(errno));
472 }
473 return res;
474}
475
Chenbo Feng89c12f12018-03-21 10:29:18 -0700476int TrafficController::updateOwnerMapEntry(const base::unique_fd& map_fd, uid_t uid,
477 FirewallRule rule, FirewallType type) {
478 int res = 0;
479 if (uid == NONEXISTENT_UID) {
480 ALOGE("This uid should never exist in maps: %u", uid);
481 return -EINVAL;
482 }
483
484 if (uid == UID_MAP_ENABLED) {
485 ALOGE("This uid is reserved for map state");
486 return -EINVAL;
487 }
488
489 if ((rule == ALLOW && type == WHITELIST) || (rule == DENY && type == BLACKLIST)) {
490 uint8_t flag = (type == WHITELIST) ? BPF_PASS : BPF_DROP;
491 res = writeToMapEntry(map_fd, &uid, &flag, BPF_ANY);
492 if (res) {
493 res = -errno;
494 ALOGE("Failed to add owner rule(uid: %u): %s", uid, strerror(errno));
495 }
496 } else if ((rule == ALLOW && type == BLACKLIST) || (rule == DENY && type == WHITELIST)) {
497 res = deleteMapEntry(map_fd, &uid);
498 if (res) {
499 res = -errno;
500 ALOGE("Failed to delete owner rule(uid: %u): %s", uid, strerror(errno));
501 }
502 } else {
503 //Cannot happen.
504 return -EINVAL;
505 }
506 return res;
507}
508
509int TrafficController::changeUidOwnerRule(ChildChain chain, uid_t uid, FirewallRule rule,
510 FirewallType type) {
511 std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
512 if (!ebpfSupported) {
513 ALOGE("bpf is not set up, should use iptables rule");
514 return -ENOSYS;
515 }
516 switch (chain) {
517 case DOZABLE:
518 return updateOwnerMapEntry(mDozableUidMap, uid, rule, type);
519 case STANDBY:
520 return updateOwnerMapEntry(mStandbyUidMap, uid, rule, type);
521 case POWERSAVE:
522 return updateOwnerMapEntry(mPowerSaveUidMap, uid, rule, type);
523 case NONE:
524 default:
525 return -EINVAL;
526 }
527}
528
529int TrafficController::replaceUidsInMap(const base::unique_fd& map_fd,
530 const std::vector<int32_t> &uids,
531 FirewallRule rule, FirewallType type) {
532 std::set<int32_t> uidSet(uids.begin(), uids.end());
533 std::vector<uint32_t> uidsToDelete;
534 auto getUidsToDelete = [&uidsToDelete, &uidSet](void *key, const base::unique_fd&) {
535 uint32_t uid = *(uint32_t *)key;
536 if (uid != UID_MAP_ENABLED && uidSet.find((int32_t)uid) == uidSet.end()) {
537 uidsToDelete.push_back(uid);
538 }
539 return 0;
540 };
541 uint32_t nonExistentKey = NONEXISTENT_UID;
542 uint8_t dummyValue;
543 int ret = bpfIterateMap(nonExistentKey, dummyValue, map_fd, getUidsToDelete);
544
545 if (ret) return ret;
546
547 for(auto uid : uidsToDelete) {
548 if(deleteMapEntry(map_fd, &uid)) {
549 ret = -errno;
550 ALOGE("Delete uid(%u) from owner Map %d failed: %s", uid, map_fd.get(),
551 strerror(errno));
552 return -errno;
553 }
554 }
555
556 for (auto uid : uids) {
557 ret = updateOwnerMapEntry(map_fd, uid, rule, type);
558 if (ret) {
559 ALOGE("Failed to add owner rule(uid: %u, map: %d)", uid, map_fd.get());
560 return ret;
561 }
562 }
563 return 0;
564}
565
566int TrafficController::replaceUidOwnerMap(const std::string& name, bool isWhitelist,
567 const std::vector<int32_t>& uids) {
568 std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
569 FirewallRule rule;
570 FirewallType type;
571 if (isWhitelist) {
572 type = WHITELIST;
573 rule = ALLOW;
574 } else {
575 type = BLACKLIST;
576 rule = DENY;
577 }
578 int ret;
579 if (!name.compare(FirewallController::LOCAL_DOZABLE)) {
580 ret = replaceUidsInMap(mDozableUidMap, uids, rule, type);
581 } else if (!name.compare(FirewallController::LOCAL_STANDBY)) {
582 ret = replaceUidsInMap(mStandbyUidMap, uids, rule, type);
583 } else if (!name.compare(FirewallController::LOCAL_POWERSAVE)) {
584 ret = replaceUidsInMap(mPowerSaveUidMap, uids, rule, type);
585 } else {
586 ALOGE("unknown chain name: %s", name.c_str());
587 return -EINVAL;
588 }
589 if (ret) {
590 ALOGE("Failed to clean up chain: %s: %s", name.c_str(), strerror(ret));
591 return ret;
592 }
593 return 0;
594}
595
596int TrafficController::toggleUidOwnerMap(ChildChain chain, bool enable) {
597 std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
598 uint32_t keyUid = UID_MAP_ENABLED;
599 uint8_t mapState = enable ? 1 : 0;
600 int ret;
601 switch (chain) {
602 case DOZABLE:
603 ret = writeToMapEntry(mDozableUidMap, &keyUid, &mapState, BPF_EXIST);
604 break;
605 case STANDBY:
606 ret = writeToMapEntry(mStandbyUidMap, &keyUid, &mapState, BPF_EXIST);
607 break;
608 case POWERSAVE:
609 ret = writeToMapEntry(mPowerSaveUidMap, &keyUid, &mapState, BPF_EXIST);
610 break;
611 default:
612 return -EINVAL;
613 }
614 if (ret) {
615 ret = -errno;
616 ALOGE("Failed to toggleUidOwnerMap(%d): %s", chain, strerror(errno));
617 }
618 return ret;
619}
620
Chenbo Feng07d43fe2017-12-21 14:38:51 -0800621bool TrafficController::checkBpfStatsEnable() {
622 return ebpfSupported;
623}
624
Chenbo Fengef297172018-03-26 10:53:33 -0700625std::string getProgramStatus(const char *path) {
626 int ret = access(path, R_OK);
627 if (ret == 0) {
628 return StringPrintf("OK");
629 }
630 if (ret != 0 && errno == ENOENT) {
631 return StringPrintf("program is missing at: %s", path);
632 }
633 return StringPrintf("check Program %s error: %s", path, strerror(errno));
634}
635
636std::string getMapStatus(const unique_fd& map_fd, const char *path) {
637 if (map_fd.get() < 0) {
638 return StringPrintf("map fd lost");
639 }
640 if (access(path, F_OK) != 0) {
641 return StringPrintf("map not pinned to location: %s", path);
642 }
643 return StringPrintf("OK");
644}
645
646void dumpBpfMap(std::string mapName, DumpWriter& dw, const std::string& header) {
647 dw.blankline();
648 dw.println("%s:", mapName.c_str());
649 if(!header.empty()) {
650 dw.println(header.c_str());
651 }
652}
653
654const String16 TrafficController::DUMP_KEYWORD = String16("trafficcontroller");
655
656void TrafficController::dump(DumpWriter& dw, bool verbose) {
657 std::lock_guard<std::mutex> guard(mOwnerMatchMutex);
658 dw.incIndent();
659 dw.println("TrafficController");
660
661 dw.incIndent();
662 dw.println("BPF module status: %s", ebpfSupported? "ON" : "OFF");
663
664 if (!ebpfSupported)
665 return;
666
667 dw.blankline();
668 dw.println("mCookieTagMap status: %s",
669 getMapStatus(mCookieTagMap, COOKIE_TAG_MAP_PATH).c_str());
670 dw.println("mUidCounterSetMap status: %s",
671 getMapStatus(mUidCounterSetMap, UID_COUNTERSET_MAP_PATH).c_str());
672 dw.println("mUidStatsMap status: %s", getMapStatus(mUidStatsMap, UID_STATS_MAP_PATH).c_str());
673 dw.println("mTagStatsMap status: %s", getMapStatus(mTagStatsMap, TAG_STATS_MAP_PATH).c_str());
674 dw.println("mIfaceIndexNameMap status: %s",
675 getMapStatus(mIfaceIndexNameMap, IFACE_INDEX_NAME_MAP_PATH).c_str());
676 dw.println("mIfaceStatsMap status: %s",
677 getMapStatus(mIfaceStatsMap, IFACE_STATS_MAP_PATH).c_str());
678 dw.println("mDozableUidMap status: %s",
679 getMapStatus(mDozableUidMap, DOZABLE_UID_MAP_PATH).c_str());
680 dw.println("mStandbyUidMap status: %s",
681 getMapStatus(mStandbyUidMap, STANDBY_UID_MAP_PATH).c_str());
682 dw.println("mPowerSaveUidMap status: %s",
683 getMapStatus(mPowerSaveUidMap, POWERSAVE_UID_MAP_PATH).c_str());
684
685 dw.blankline();
686 dw.println("Cgroup ingress program status: %s",
687 getProgramStatus(BPF_INGRESS_PROG_PATH).c_str());
688 dw.println("Cgroup egress program status: %s", getProgramStatus(BPF_EGRESS_PROG_PATH).c_str());
689 dw.println("xt_bpf ingress program status: %s",
690 getProgramStatus(XT_BPF_INGRESS_PROG_PATH).c_str());
691 dw.println("xt_bpf egress program status: %s",
692 getProgramStatus(XT_BPF_EGRESS_PROG_PATH).c_str());
693
694 if(!verbose) return;
695
696 dw.blankline();
697 dw.println("BPF map content:");
698
699 dw.incIndent();
700
701 // Print CookieTagMap content.
702 dumpBpfMap("mCookieTagMap", dw, "");
703 const uint64_t nonExistentCookie = NONEXIST_COOKIE;
704 UidTag dummyUidTag;
705 auto printCookieTagInfo = [&dw](void *key, void *value) {
706 UidTag uidTagEntry = *(UidTag *) value;
707 uint64_t cookie = *(uint64_t *) key;
708 dw.println("cookie=%" PRIu64 " tag=0x%x uid=%u", cookie, uidTagEntry.tag, uidTagEntry.uid);
709 return 0;
710 };
711 int ret = bpfIterateMapWithValue(nonExistentCookie, dummyUidTag, mCookieTagMap,
712 printCookieTagInfo);
713 if (ret) {
714 dw.println("mCookieTagMap print end with error: %s", strerror(-ret));
715 }
716
717 // Print UidCounterSetMap Content
718 dumpBpfMap("mUidCounterSetMap", dw, "");
719 const uint32_t nonExistentUid = NONEXISTENT_UID;
720 uint32_t dummyUidInfo;
721 auto printUidInfo = [&dw](void *key, void *value) {
722 uint8_t setting = *(uint8_t *) value;
723 uint32_t uid = *(uint32_t *) key;
724 dw.println("%u %u", uid, setting);
725 return 0;
726 };
727 ret = bpfIterateMapWithValue(nonExistentUid, dummyUidInfo, mUidCounterSetMap, printUidInfo);
728 if (ret) {
729 dw.println("mUidCounterSetMap print end with error: %s", strerror(-ret));
730 }
731
732 // Print uidStatsMap content
733 std::string statsHeader = StringPrintf("ifaceIndex ifaceName tag_hex uid_int cnt_set rxBytes"
734 " rxPackets txBytes txPackets");
735 dumpBpfMap("mUidStatsMap", dw, statsHeader);
736 const struct StatsKey nonExistentStatsKey = NONEXISTENT_STATSKEY;
737 struct StatsValue dummyStatsValue;
738 auto printStatsInfo = [&dw, this](void *key, void *value) {
739 StatsValue statsEntry = *(StatsValue *) value;
740 StatsKey keyInfo = *(StatsKey *) key;
741 char ifname[IFNAMSIZ];
742 uint32_t ifIndex = keyInfo.ifaceIndex;
743 if (bpf::findMapEntry(mIfaceIndexNameMap, &ifIndex, &ifname) < 0) {
744 strlcpy(ifname, "unknown", sizeof(ifname));
745 }
746 dw.println("%u %s 0x%x %u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifIndex, ifname,
747 keyInfo.tag, keyInfo.uid, keyInfo.counterSet, statsEntry.rxBytes,
748 statsEntry.rxPackets, statsEntry.txBytes, statsEntry.txPackets);
749 return 0;
750 };
751 ret = bpfIterateMapWithValue(nonExistentStatsKey, dummyStatsValue, mUidStatsMap,
752 printStatsInfo);
753 if (ret) {
754 dw.println("mUidStatsMap print end with error: %s", strerror(-ret));
755 }
756
757 // Print TagStatsMap content.
758 dumpBpfMap("mTagStatsMap", dw, statsHeader);
759 ret = bpfIterateMapWithValue(nonExistentStatsKey, dummyStatsValue, mTagStatsMap,
760 printStatsInfo);
761 if (ret) {
762 dw.println("mTagStatsMap print end with error: %s", strerror(-ret));
763 }
764
765 // Print ifaceIndexToNameMap content.
766 dumpBpfMap("mIfaceIndexNameMap", dw, "");
767 uint32_t nonExistentIface = NONEXISTENT_IFACE_STATS_KEY;
768 char dummyIface[IFNAMSIZ];
769 auto printIfaceNameInfo = [&dw](void *key, void *value) {
770 char *ifname = (char *) value;
771 uint32_t ifaceIndex = *(uint32_t *)key;
772 dw.println("ifaceIndex=%u ifaceName=%s", ifaceIndex, ifname);
773 return 0;
774 };
775 ret = bpfIterateMapWithValue(nonExistentIface, dummyIface, mIfaceIndexNameMap,
776 printIfaceNameInfo);
777 if (ret) {
778 dw.println("mIfaceIndexNameMap print end with error: %s", strerror(-ret));
779 }
780
781 // Print ifaceStatsMap content
782 std::string ifaceStatsHeader = StringPrintf("ifaceIndex ifaceName rxBytes rxPackets txBytes"
783 " txPackets");
784 dumpBpfMap("mIfaceStatsMap:", dw, ifaceStatsHeader);
785 auto printIfaceStatsInfo = [&dw, this] (void *key, void *value) {
786 StatsValue statsEntry = *(StatsValue *) value;
787 uint32_t ifaceIndex = *(uint32_t *) key;
788 char ifname[IFNAMSIZ];
789 if (bpf::findMapEntry(mIfaceIndexNameMap, key, ifname) < 0) {
790 strlcpy(ifname, "unknown", sizeof(ifname));
791 }
792 dw.println("%u %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, ifaceIndex, ifname,
793 statsEntry.rxBytes, statsEntry.rxPackets, statsEntry.txBytes,
794 statsEntry.txPackets);
795 return 0;
796 };
797 ret = bpfIterateMapWithValue(nonExistentIface, dummyStatsValue, mIfaceStatsMap,
798 printIfaceStatsInfo);
799 if (ret) {
800 dw.println("mIfaceStatsMap print end with error: %s", strerror(-ret));
801 }
802
803 // Print owner match uid maps
804 uint8_t dummyOwnerInfo;
805 dumpBpfMap("mDozableUidMap", dw, "");
806 ret = bpfIterateMapWithValue(nonExistentUid, dummyOwnerInfo, mDozableUidMap, printUidInfo);
807 if (ret) {
808 dw.println("mDozableUidMap print end with error: %s", strerror(-ret));
809 }
810
811 dumpBpfMap("mStandbyUidMap", dw, "");
812 ret = bpfIterateMapWithValue(nonExistentUid, dummyOwnerInfo, mStandbyUidMap, printUidInfo);
813 if (ret) {
814 dw.println("mDozableUidMap print end with error: %s", strerror(-ret));
815 }
816
817 dumpBpfMap("mPowerSaveUidMap", dw, "");
818 ret = bpfIterateMapWithValue(nonExistentUid, dummyOwnerInfo, mPowerSaveUidMap, printUidInfo);
819 if (ret) {
820 dw.println("mDozableUidMap print end with error: %s", strerror(-ret));
821 }
822
823 dw.decIndent();
824
825 dw.decIndent();
826
827 dw.decIndent();
828
829}
830
Chenbo Fengf2759682017-10-10 17:31:57 -0700831} // namespace net
832} // namespace android