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