blob: c9ed75f7c8d51e3770a5cc54c8009c395c1d01c4 [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 Feng33cc1032017-10-23 15:16:37 -070017#include <inttypes.h>
Chenbo Fengf2759682017-10-10 17:31:57 -070018#include <linux/bpf.h>
19#include <linux/if_ether.h>
20#include <linux/in.h>
21#include <linux/inet_diag.h>
22#include <linux/unistd.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/socket.h>
Chenbo Feng33cc1032017-10-23 15:16:37 -070026#include <sys/utsname.h>
Chenbo Fengf2759682017-10-10 17:31:57 -070027
28#include "BpfProgSets.h"
29#include "BpfUtils.h"
30#include "TrafficController.h"
31
32#define LOG_TAG "Netd"
33#include "log/log.h"
Chenbo Feng33cc1032017-10-23 15:16:37 -070034#include "qtaguid/qtaguid.h"
Chenbo Fengf2759682017-10-10 17:31:57 -070035
36using namespace android::net::bpf;
37using namespace android::net::bpf_prog;
38
39namespace android {
40namespace net {
41
42int TrafficController::start() {
43 int ret;
Chenbo Feng33cc1032017-10-23 15:16:37 -070044 struct utsname buf;
45 int kernel_version_major;
46 int kernel_version_minor;
47
48 ret = uname(&buf);
49 if (ret) {
50 ret = -errno;
51 ALOGE("Get system information failed: %s\n", strerror(errno));
52 return ret;
53 }
54 ret = sscanf(buf.release, "%d.%d", &kernel_version_major, &kernel_version_minor);
55 if (ret >= 2 && ((kernel_version_major == 4 && kernel_version_minor >= 9) ||
56 (kernel_version_major > 4))) {
57 // Turn off the eBPF feature temporarily since the selinux rules and kernel changes are not
58 // landed yet.
59 // TODO: turn back on when all the other dependencies are ready.
60 ebpfSupported = false;
61 return 0;
62 } else {
63 ebpfSupported = false;
64 return 0;
65 }
Chenbo Fengf2759682017-10-10 17:31:57 -070066 ALOGI("START to load TrafficController\n");
67 mCookieTagMap = setUpBPFMap(sizeof(uint64_t), sizeof(struct UidTag), COOKIE_UID_MAP_SIZE,
68 COOKIE_UID_MAP_PATH, BPF_MAP_TYPE_HASH);
69 if (mCookieTagMap < 0) {
70 ret = -errno;
71 ALOGE("mCookieTagMap load failed\n");
72 return ret;
73 }
74 mUidCounterSetMap = setUpBPFMap(sizeof(uint32_t), sizeof(uint32_t), UID_COUNTERSET_MAP_SIZE,
75 UID_COUNTERSET_MAP_PATH, BPF_MAP_TYPE_HASH);
76 if (mUidCounterSetMap < 0) {
77 ret = -errno;
78 ALOGE("mUidCounterSetMap load failed\n");
79 return ret;
80 }
81 mUidStatsMap = setUpBPFMap(sizeof(struct StatsKey), sizeof(struct Stats), UID_STATS_MAP_SIZE,
82 UID_STATS_MAP_PATH, BPF_MAP_TYPE_HASH);
83 if (mUidStatsMap < 0) {
84 ret = -errno;
85 ALOGE("mUidStatsMap load failed\n");
86 return ret;
87 }
88 mTagStatsMap = setUpBPFMap(sizeof(struct StatsKey), sizeof(struct Stats), TAG_STATS_MAP_SIZE,
89 TAG_STATS_MAP_PATH, BPF_MAP_TYPE_HASH);
90 if (mTagStatsMap < 0) {
91 ret = -errno;
92 ALOGE("mTagStatsMap load failed\n");
93 return ret;
94 }
95
96 /* When netd restart from a crash without total system reboot, the program
97 * is still attached to the cgroup, detach it so the program can be freed
98 * and we can load and attach new program into the target cgroup.
99 *
100 * TODO: Scrape existing socket when run-time restart and clean up the map
101 * if the socket no longer exist
102 */
103
104 int cg_fd = open(CGROUP_ROOT_PATH, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
105 if (cg_fd < 0) {
106 ret = -errno;
107 ALOGE("Failed to open the cgroup directory\n");
108 return ret;
109 }
110
111 ret = detachProgram(BPF_CGROUP_INET_EGRESS, cg_fd);
112 ret = detachProgram(BPF_CGROUP_INET_INGRESS, cg_fd);
113
114 mInProgFd = loadIngressProg(mCookieTagMap, mUidStatsMap, mTagStatsMap, mUidCounterSetMap);
115 if (mInProgFd < 0) {
116 ret = -errno;
117 ALOGE("Load ingress program failed\n");
118 return ret;
119 }
120
121 mOutProgFd = loadEgressProg(mCookieTagMap, mUidStatsMap, mTagStatsMap, mUidCounterSetMap);
122 if (mOutProgFd < 0) {
123 ret = -errno;
124 ALOGE("load egress program failed\n");
125 return ret;
126 }
127
128 ret = attachProgram(BPF_CGROUP_INET_EGRESS, mOutProgFd, cg_fd);
129 if (ret) {
130 ret = -errno;
131 ALOGE("egress program attach failed: %s\n", strerror(errno));
132 return ret;
133 }
134
135 ret = attachProgram(BPF_CGROUP_INET_INGRESS, mInProgFd, cg_fd);
136 if (ret) {
137 ret = -errno;
138 ALOGE("ingress program attach failed: %s\n", strerror(errno));
139 return ret;
140 }
141 close(cg_fd);
142 return 0;
143}
144
145uint64_t getSocketCookie(int sockFd) {
146 uint64_t sock_cookie;
147 socklen_t cookie_len = sizeof(sock_cookie);
148 int res = getsockopt(sockFd, SOL_SOCKET, SO_COOKIE, &sock_cookie, &cookie_len);
149 if (res < 0) {
150 res = -errno;
151 ALOGE("Failed to get socket cookie: %s\n", strerror(errno));
152 errno = -res;
153 // 0 is an invalid cookie. See INET_DIAG_NOCOOKIE.
154 return 0;
155 }
156 return sock_cookie;
157}
158
159int TrafficController::tagSocket(int sockFd, uint32_t tag, uid_t uid) {
Chenbo Feng33cc1032017-10-23 15:16:37 -0700160
161 if (legacy_tagSocket(sockFd, tag, uid))
162 return -errno;
163 if (!ebpfSupported)
164 return 0;
165
Chenbo Fengf2759682017-10-10 17:31:57 -0700166 uint64_t sock_cookie = getSocketCookie(sockFd);
167 if (sock_cookie == INET_DIAG_NOCOOKIE) return -errno;
168 UidTag newKey = {.uid = (uint32_t)uid, .tag = tag};
169
170 // Update the tag information of a socket to the cookieUidMap. Use BPF_ANY
171 // flag so it will insert a new entry to the map if that value doesn't exist
172 // yet. And update the tag if there is already a tag stored. Since the eBPF
173 // program in kernel only read this map, and is protected by rcu read lock. It
174 // should be fine to cocurrently update the map while eBPF program is running.
175 int res = writeToMapEntry(mCookieTagMap, &sock_cookie, &newKey, BPF_ANY);
176 if (res < 0) {
177 res = -errno;
178 ALOGE("Failed to tag the socket: %s\n", strerror(errno));
179 }
180
181 return res;
182}
183
184int TrafficController::untagSocket(int sockFd) {
Chenbo Feng33cc1032017-10-23 15:16:37 -0700185
186 if (legacy_untagSocket(sockFd))
187 return -errno;
188 if (!ebpfSupported) return 0;
Chenbo Fengf2759682017-10-10 17:31:57 -0700189 uint64_t sock_cookie = getSocketCookie(sockFd);
190
191 if (sock_cookie == INET_DIAG_NOCOOKIE) return -errno;
192 int res = deleteMapEntry(mCookieTagMap, &sock_cookie);
193 if (res) {
194 res = -errno;
195 ALOGE("Failed to untag socket: %s\n", strerror(errno));
196 }
197 return res;
198}
199
200int TrafficController::setCounterSet(int counterSetNum, uid_t uid) {
201 if (counterSetNum < 0 || counterSetNum >= COUNTERSETS_LIMIT) return -EINVAL;
Chenbo Fengf2759682017-10-10 17:31:57 -0700202 int res;
Chenbo Feng33cc1032017-10-23 15:16:37 -0700203 if (legacy_setCounterSet(counterSetNum, uid))
204 return -errno;
205 if (!ebpfSupported) return 0;;
Chenbo Fengf2759682017-10-10 17:31:57 -0700206 if (counterSetNum == 0) {
207 res = deleteMapEntry(mUidCounterSetMap, &uid);
208 if (res == 0 || (res == -1 && errno == ENOENT)) {
209 return 0;
210 } else {
211 ALOGE("Failed to delete the counterSet: %s\n", strerror(errno));
212 return -errno;
213 }
214 }
215
216 res = writeToMapEntry(mUidCounterSetMap, &uid, &counterSetNum, BPF_ANY);
217 if (res < 0) {
218 res = -errno;
219 ALOGE("Failed to set the counterSet: %s\n", strerror(errno));
220 }
221 return res;
222}
223
224int TrafficController::deleteTagData(uint32_t tag, uid_t uid) {
225 int res = 0;
Chenbo Feng33cc1032017-10-23 15:16:37 -0700226
227 if (legacy_deleteTagData(tag, uid))
228 return -errno;
229 if (!ebpfSupported) return 0;
230
Chenbo Fengf2759682017-10-10 17:31:57 -0700231 uint64_t curCookie = 0;
232 uint64_t nextCookie = 0;
233 UidTag tmp_uidtag;
234 bool end = false;
235
236 // First we go through the cookieTagMap to delete the target uid tag combanition. Or delete all
237 // the tags related to the uid if the tag is 0
238 while (!end && getNextMapKey(mCookieTagMap, &curCookie, &nextCookie) > -1) {
239 curCookie = nextCookie;
240 res = findMapEntry(mCookieTagMap, &curCookie, &tmp_uidtag);
241 if (res < 0) {
242 res = -errno;
243 ALOGE("Failed to get tag info(cookie = %" PRIu64": %s\n", curCookie, strerror(errno));
244 return res;
245 }
246
247 if (tmp_uidtag.uid == uid && (tmp_uidtag.tag == tag || tag == 0)) {
248 // To prevent we iterrate the map again from the begining, we firstly get the key next
249 // to the key we are going to delete here. And use it as the key when we get next entry.
250 res = getNextMapKey(mCookieTagMap, &curCookie, &nextCookie);
251 if (res == -1) end = true;
252 res = deleteMapEntry(mCookieTagMap, &curCookie);
253 if (res != 0 || (res == -1 && errno != ENOENT)) {
254 res = -errno;
255 ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", uid, tag, strerror(errno));
256 return res;
257 }
258 curCookie = nextCookie;
259 }
260 }
261
262 // Now we go through the Tag stats map and delete the data entry with correct uid and tag
263 // combanition. Or all tag stats under that uid if the target tag is 0.
264 struct StatsKey curKey, nextKey;
265 memset(&curKey, 0, sizeof(curKey));
266 curKey.uid = DEFAULT_OVERFLOWUID;
267 end = false;
268 while (getNextMapKey(mTagStatsMap, &curKey, &nextKey) > -1) {
269 curKey = nextKey;
270 if (curKey.uid == uid && (curKey.tag == tag || tag == 0)) {
271 res = getNextMapKey(mTagStatsMap, &curKey, &nextKey);
272 if (res == -1) end = true;
273 res = deleteMapEntry(mTagStatsMap, &curKey);
274 if (res != 0 || (res == -1 && errno != ENOENT)) {
275 res = -errno;
276 ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", uid, tag, strerror(errno));
277 return res;
278 }
279 curKey = nextKey;
280 }
281 }
282
283 // If the tag is not zero, we already deleted all the data entry required. If tag is 0, we also
284 // need to delete the stats stored in uidStatsMap
285 if (tag != 0) return res;
286 memset(&curKey, 0, sizeof(curKey));
287 curKey.uid = DEFAULT_OVERFLOWUID;
288 end = false;
289 while (getNextMapKey(mUidStatsMap, &curKey, &nextKey) > -1) {
290 curKey = nextKey;
291 if (curKey.uid == uid) {
292 res = getNextMapKey(mTagStatsMap, &curKey, &nextKey);
293 if (res == -1) end = true;
294 res = deleteMapEntry(mUidStatsMap, &curKey);
295 if (res != 0 || (res == -1 && errno != ENOENT)) {
296 res = -errno;
297 ALOGE("Failed to delete data(uid=%u, tag=%u): %s\n", uid, tag, strerror(errno));
298 return res;
299 }
300 curKey = nextKey;
301 }
302 }
303 return res;
304}
305
Chenbo Fengf2759682017-10-10 17:31:57 -0700306} // namespace net
307} // namespace android