blob: 081317b728bb40c9b7e0b87af7ea6bd13d87ebe7 [file] [log] [blame]
Chenbo Feng75b410b2018-10-10 15:01:19 -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#define LOG_TAG "BpfUtils"
18
Bernie Innocenti26ffded2018-10-19 15:41:53 +090019#include "bpf/BpfUtils.h"
20
Chenbo Feng75b410b2018-10-10 15:01:19 -070021#include <elf.h>
22#include <inttypes.h>
23#include <linux/bpf.h>
24#include <linux/if_ether.h>
25#include <linux/in.h>
Chenbo Feng9cd8f142018-12-04 16:54:56 -080026#include <linux/pfkeyv2.h>
Chenbo Feng75b410b2018-10-10 15:01:19 -070027#include <stdlib.h>
28#include <string.h>
29#include <sys/mman.h>
Chenbo Feng0a1a9a12019-04-09 12:05:04 -070030#include <sys/resource.h>
Chenbo Feng75b410b2018-10-10 15:01:19 -070031#include <sys/socket.h>
32#include <sys/stat.h>
33#include <sys/utsname.h>
34#include <sstream>
35#include <string>
36
37#include <android-base/properties.h>
Chenbo Feng75b410b2018-10-10 15:01:19 -070038#include <android-base/unique_fd.h>
Bernie Innocenti26ffded2018-10-19 15:41:53 +090039#include <log/log.h>
Chenbo Feng75b410b2018-10-10 15:01:19 -070040#include <netdutils/MemBlock.h>
41#include <netdutils/Slice.h>
Suren Baghdasaryan9217ccb2018-12-19 17:29:13 -080042#include <processgroup/processgroup.h>
Chenbo Feng75b410b2018-10-10 15:01:19 -070043
44using android::base::GetUintProperty;
Chenbo Feng75b410b2018-10-10 15:01:19 -070045using android::base::unique_fd;
46using android::netdutils::MemBlock;
47using android::netdutils::Slice;
Chenbo Feng75b410b2018-10-10 15:01:19 -070048
Chenbo Feng9cd8f142018-12-04 16:54:56 -080049// The buffer size for the buffer that records program loading logs, needs to be large enough for
50// the largest kernel program.
51constexpr size_t LOG_BUF_SIZE = 0x20000;
Chenbo Feng75b410b2018-10-10 15:01:19 -070052
53namespace android {
54namespace bpf {
55
56/* The bpf_attr is a union which might have a much larger size then the struct we are using, while
57 * The inline initializer only reset the field we are using and leave the reset of the memory as
58 * is. The bpf kernel code will performs a much stricter check to ensure all unused field is 0. So
59 * this syscall will normally fail with E2BIG if we don't do a memset to bpf_attr.
60 */
61bool operator==(const StatsKey& lhs, const StatsKey& rhs) {
62 return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag) && (lhs.counterSet == rhs.counterSet) &&
63 (lhs.ifaceIndex == rhs.ifaceIndex));
64}
65
66bool operator==(const UidTag& lhs, const UidTag& rhs) {
67 return ((lhs.uid == rhs.uid) && (lhs.tag == rhs.tag));
68}
69
70bool operator==(const StatsValue& lhs, const StatsValue& rhs) {
71 return ((lhs.rxBytes == rhs.rxBytes) && (lhs.txBytes == rhs.txBytes) &&
72 (lhs.rxPackets == rhs.rxPackets) && (lhs.txPackets == rhs.txPackets));
73}
74
75int bpf(int cmd, Slice bpfAttr) {
76 return syscall(__NR_bpf, cmd, bpfAttr.base(), bpfAttr.size());
77}
78
79int createMap(bpf_map_type map_type, uint32_t key_size, uint32_t value_size, uint32_t max_entries,
80 uint32_t map_flags) {
81 bpf_attr attr;
82 memset(&attr, 0, sizeof(attr));
83 attr.map_type = map_type;
84 attr.key_size = key_size;
85 attr.value_size = value_size;
86 attr.max_entries = max_entries;
87 attr.map_flags = map_flags;
88
89 return bpf(BPF_MAP_CREATE, Slice(&attr, sizeof(attr)));
90}
91
92int writeToMapEntry(const base::unique_fd& map_fd, void* key, void* value, uint64_t flags) {
93 bpf_attr attr;
94 memset(&attr, 0, sizeof(attr));
95 attr.map_fd = map_fd.get();
96 attr.key = ptr_to_u64(key);
97 attr.value = ptr_to_u64(value);
98 attr.flags = flags;
99
100 return bpf(BPF_MAP_UPDATE_ELEM, Slice(&attr, sizeof(attr)));
101}
102
103int findMapEntry(const base::unique_fd& map_fd, void* key, void* value) {
104 bpf_attr attr;
105 memset(&attr, 0, sizeof(attr));
106 attr.map_fd = map_fd.get();
107 attr.key = ptr_to_u64(key);
108 attr.value = ptr_to_u64(value);
109
110 return bpf(BPF_MAP_LOOKUP_ELEM, Slice(&attr, sizeof(attr)));
111}
112
113int deleteMapEntry(const base::unique_fd& map_fd, void* key) {
114 bpf_attr attr;
115 memset(&attr, 0, sizeof(attr));
116 attr.map_fd = map_fd.get();
117 attr.key = ptr_to_u64(key);
118
119 return bpf(BPF_MAP_DELETE_ELEM, Slice(&attr, sizeof(attr)));
120}
121
122int getNextMapKey(const base::unique_fd& map_fd, void* key, void* next_key) {
123 bpf_attr attr;
124 memset(&attr, 0, sizeof(attr));
125 attr.map_fd = map_fd.get();
126 attr.key = ptr_to_u64(key);
127 attr.next_key = ptr_to_u64(next_key);
128
129 return bpf(BPF_MAP_GET_NEXT_KEY, Slice(&attr, sizeof(attr)));
130}
131
132int getFirstMapKey(const base::unique_fd& map_fd, void* firstKey) {
133 bpf_attr attr;
134 memset(&attr, 0, sizeof(attr));
135 attr.map_fd = map_fd.get();
136 attr.key = 0;
137 attr.next_key = ptr_to_u64(firstKey);
138
139 return bpf(BPF_MAP_GET_NEXT_KEY, Slice(&attr, sizeof(attr)));
140}
141
142int bpfProgLoad(bpf_prog_type prog_type, Slice bpf_insns, const char* license,
143 uint32_t kern_version, Slice bpf_log) {
144 bpf_attr attr;
145 memset(&attr, 0, sizeof(attr));
146 attr.prog_type = prog_type;
147 attr.insns = ptr_to_u64(bpf_insns.base());
148 attr.insn_cnt = bpf_insns.size() / sizeof(struct bpf_insn);
149 attr.license = ptr_to_u64((void*)license);
150 attr.log_buf = ptr_to_u64(bpf_log.base());
151 attr.log_size = bpf_log.size();
152 attr.log_level = DEFAULT_LOG_LEVEL;
153 attr.kern_version = kern_version;
154 int ret = bpf(BPF_PROG_LOAD, Slice(&attr, sizeof(attr)));
155
156 if (ret < 0) {
157 std::string prog_log = netdutils::toString(bpf_log);
158 std::istringstream iss(prog_log);
159 for (std::string line; std::getline(iss, line);) {
160 ALOGE("%s", line.c_str());
161 }
162 }
163 return ret;
164}
165
166int bpfFdPin(const base::unique_fd& map_fd, const char* pathname) {
167 bpf_attr attr;
168 memset(&attr, 0, sizeof(attr));
169 attr.pathname = ptr_to_u64((void*)pathname);
170 attr.bpf_fd = map_fd.get();
171
172 return bpf(BPF_OBJ_PIN, Slice(&attr, sizeof(attr)));
173}
174
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800175int bpfFdGet(const char* pathname, uint32_t flag) {
Chenbo Feng75b410b2018-10-10 15:01:19 -0700176 bpf_attr attr;
177 memset(&attr, 0, sizeof(attr));
178 attr.pathname = ptr_to_u64((void*)pathname);
179 attr.file_flags = flag;
180 return bpf(BPF_OBJ_GET, Slice(&attr, sizeof(attr)));
181}
182
Chenbo Fengc1dd7642018-12-22 11:41:20 -0800183int mapRetrieve(const char* pathname, uint32_t flag) {
184 return bpfFdGet(pathname, flag);
185}
186
Chenbo Feng75b410b2018-10-10 15:01:19 -0700187int attachProgram(bpf_attach_type type, uint32_t prog_fd, uint32_t cg_fd) {
188 bpf_attr attr;
189 memset(&attr, 0, sizeof(attr));
190 attr.target_fd = cg_fd;
191 attr.attach_bpf_fd = prog_fd;
192 attr.attach_type = type;
193
194 return bpf(BPF_PROG_ATTACH, Slice(&attr, sizeof(attr)));
195}
196
197int detachProgram(bpf_attach_type type, uint32_t cg_fd) {
198 bpf_attr attr;
199 memset(&attr, 0, sizeof(attr));
200 attr.target_fd = cg_fd;
201 attr.attach_type = type;
202
203 return bpf(BPF_PROG_DETACH, Slice(&attr, sizeof(attr)));
204}
205
206uint64_t getSocketCookie(int sockFd) {
207 uint64_t sock_cookie;
208 socklen_t cookie_len = sizeof(sock_cookie);
209 int res = getsockopt(sockFd, SOL_SOCKET, SO_COOKIE, &sock_cookie, &cookie_len);
210 if (res < 0) {
211 res = -errno;
212 ALOGE("Failed to get socket cookie: %s\n", strerror(errno));
213 errno = -res;
214 // 0 is an invalid cookie. See sock_gen_cookie.
215 return NONEXISTENT_COOKIE;
216 }
217 return sock_cookie;
218}
219
Chenbo Feng9cd8f142018-12-04 16:54:56 -0800220int synchronizeKernelRCU() {
221 // This is a temporary hack for network stats map swap on devices running
222 // 4.9 kernels. The kernel code of socket release on pf_key socket will
223 // explicitly call synchronize_rcu() which is exactly what we need.
224 int pfSocket = socket(AF_KEY, SOCK_RAW | SOCK_CLOEXEC, PF_KEY_V2);
225
226 if (pfSocket < 0) {
227 int ret = -errno;
228 ALOGE("create PF_KEY socket failed: %s", strerror(errno));
229 return ret;
230 }
231
232 // When closing socket, synchronize_rcu() gets called in sock_release().
233 if (close(pfSocket)) {
234 int ret = -errno;
235 ALOGE("failed to close the PF_KEY socket: %s", strerror(errno));
236 return ret;
237 }
238 return 0;
239}
240
Chenbo Feng0a1a9a12019-04-09 12:05:04 -0700241int setrlimitForTest() {
242 // Set the memory rlimit for the test process if the default MEMLOCK rlimit is not enough.
243 struct rlimit limit = {
244 .rlim_cur = TEST_LIMIT,
245 .rlim_max = TEST_LIMIT,
246 };
247 int res = setrlimit(RLIMIT_MEMLOCK, &limit);
248 if (res) {
249 ALOGE("Failed to set the default MEMLOCK rlimit: %s", strerror(errno));
250 }
251 return res;
252}
253
Chenbo Feng79b7e612018-12-11 12:24:23 -0800254std::string BpfLevelToString(BpfLevel bpfLevel) {
255 switch (bpfLevel) {
256 case BpfLevel::NONE: return "NONE_SUPPORT";
257 case BpfLevel::BASIC: return "BPF_LEVEL_BASIC";
258 case BpfLevel::EXTENDED: return "BPF_LEVEL_EXTENDED";
259 // No default statement. We want to see errors of the form:
260 // "enumeration value 'BPF_LEVEL_xxx' not handled in switch [-Werror,-Wswitch]".
261 }
262}
263
264BpfLevel getBpfSupportLevel() {
Chenbo Feng75b410b2018-10-10 15:01:19 -0700265 struct utsname buf;
266 int kernel_version_major;
267 int kernel_version_minor;
268
269 uint64_t api_level = GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
270 if (api_level == 0) {
271 ALOGE("Cannot determine initial API level of the device");
272 api_level = GetUintProperty<uint64_t>("ro.build.version.sdk", 0);
273 }
274
Chenbo Feng79b7e612018-12-11 12:24:23 -0800275 // Check if the device is shipped originally with android P.
276 if (api_level < MINIMUM_API_REQUIRED) return BpfLevel::NONE;
277
Chenbo Feng75b410b2018-10-10 15:01:19 -0700278 int ret = uname(&buf);
279 if (ret) {
Chenbo Feng79b7e612018-12-11 12:24:23 -0800280 return BpfLevel::NONE;
Chenbo Feng75b410b2018-10-10 15:01:19 -0700281 }
282 char dummy;
283 ret = sscanf(buf.release, "%d.%d%c", &kernel_version_major, &kernel_version_minor, &dummy);
Chenbo Feng79b7e612018-12-11 12:24:23 -0800284 // Check the device kernel version
285 if (ret < 2) return BpfLevel::NONE;
286 if (kernel_version_major > 4 || (kernel_version_major == 4 && kernel_version_minor >= 14))
287 return BpfLevel::EXTENDED;
288 if (kernel_version_major == 4 && kernel_version_minor >= 9) return BpfLevel::BASIC;
289
290 return BpfLevel::NONE;
Chenbo Feng75b410b2018-10-10 15:01:19 -0700291}
292
293int loadAndPinProgram(BpfProgInfo* prog, Slice progBlock) {
294 // Program doesn't exist. Try to load it.
295 char bpf_log_buf[LOG_BUF_SIZE];
296 Slice bpfLog = Slice(bpf_log_buf, sizeof(bpf_log_buf));
297 prog->fd.reset(bpfProgLoad(prog->loadType, progBlock, "Apache 2.0", 0, bpfLog));
298 if (prog->fd < 0) {
299 int ret = -errno;
300 ALOGE("load %s failed: %s", prog->name, strerror(errno));
301 return ret;
302 }
303 if (prog->attachType == BPF_CGROUP_INET_EGRESS || prog->attachType == BPF_CGROUP_INET_INGRESS) {
Suren Baghdasaryan9217ccb2018-12-19 17:29:13 -0800304 std::string cg2_path;
305 if (!CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cg2_path)) {
306 int ret = -errno;
307 ALOGE("Failed to find cgroup v2 root");
308 return ret;
309 }
310 unique_fd cg_fd(open(cg2_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
Chenbo Feng75b410b2018-10-10 15:01:19 -0700311 if (cg_fd < 0) {
312 int ret = -errno;
313 ALOGE("Failed to open the cgroup directory");
314 return ret;
315 }
316 int ret = android::bpf::attachProgram(prog->attachType, prog->fd, cg_fd);
317 if (ret) {
318 ret = -errno;
319 ALOGE("%s attach failed: %s", prog->name, strerror(errno));
320 return ret;
321 }
322 }
323 if (prog->path) {
324 int ret = android::bpf::bpfFdPin(prog->fd, prog->path);
325 if (ret) {
326 ret = -errno;
327 ALOGE("Pin %s as file %s failed: %s", prog->name, prog->path, strerror(errno));
328 return ret;
329 }
330 }
331 return 0;
332}
333
334int extractAndLoadProg(BpfProgInfo* prog, Elf64_Shdr* sectionPtr, Slice fileContents,
335 const std::vector<BpfMapInfo>& mapPatterns) {
Chenbo Feng1f20ad32018-11-26 15:18:46 -0800336 uint64_t progSize = (uint64_t)sectionPtr->sh_size;
Chenbo Feng75b410b2018-10-10 15:01:19 -0700337 Slice progSection = take(drop(fileContents, sectionPtr->sh_offset), progSize);
338 if (progSection.size() < progSize) {
339 ALOGE("programSection out of bound");
340 return -EINVAL;
341 }
342 MemBlock progCopy(progSection);
343 if (progCopy.get().size() != progSize) {
344 ALOGE("program cannot be extracted");
345 return -EINVAL;
346 }
347 Slice remaining = progCopy.get();
348 while (remaining.size() >= MAP_CMD_SIZE) {
349 // Scan the program, examining all possible places that might be the start of a
350 // map load operation (i.e., all bytes of value MAP_LD_CMD_HEAD).
351 // In each of these places, check whether it is the start of one of the patterns
352 // we want to replace, and if so, replace it.
353 Slice mapHead = findFirstMatching(remaining, MAP_LD_CMD_HEAD);
354 if (mapHead.size() < MAP_CMD_SIZE) break;
355 bool replaced = false;
356 for (const auto& pattern : mapPatterns) {
357 if (!memcmp(mapHead.base(), pattern.search.data(), MAP_CMD_SIZE)) {
358 memcpy(mapHead.base(), pattern.replace.data(), MAP_CMD_SIZE);
359 replaced = true;
360 break;
361 }
362 }
363 remaining = drop(mapHead, replaced ? MAP_CMD_SIZE : sizeof(uint8_t));
364 }
365 if (!(prog->path) || access(prog->path, R_OK) == -1) {
366 return loadAndPinProgram(prog, progCopy.get());
367 }
368 return 0;
369}
370
371int parsePrograms(Slice fileContents, BpfProgInfo* programs, size_t size,
372 const std::vector<BpfMapInfo>& mapPatterns) {
373 Slice elfHeader = take(fileContents, sizeof(Elf64_Ehdr));
374 if (elfHeader.size() < sizeof(Elf64_Ehdr)) {
375 ALOGE("bpf fileContents does not have complete elf header");
376 return -EINVAL;
377 }
378
Chenbo Feng1f20ad32018-11-26 15:18:46 -0800379 Elf64_Ehdr* elf = (Elf64_Ehdr*)elfHeader.base();
Chenbo Feng75b410b2018-10-10 15:01:19 -0700380 // Find section names string table. This is the section whose index is e_shstrndx.
381 if (elf->e_shstrndx == SHN_UNDEF) {
382 ALOGE("cannot locate namesSection\n");
383 return -EINVAL;
384 }
385 size_t totalSectionSize = (elf->e_shnum) * sizeof(Elf64_Shdr);
386 Slice sections = take(drop(fileContents, elf->e_shoff), totalSectionSize);
387 if (sections.size() < totalSectionSize) {
388 ALOGE("sections corrupted");
389 return -EMSGSIZE;
390 }
391
392 Slice namesSection =
Chenbo Feng1f20ad32018-11-26 15:18:46 -0800393 take(drop(sections, elf->e_shstrndx * sizeof(Elf64_Shdr)), sizeof(Elf64_Shdr));
Chenbo Feng75b410b2018-10-10 15:01:19 -0700394 if (namesSection.size() != sizeof(Elf64_Shdr)) {
395 ALOGE("namesSection corrupted");
396 return -EMSGSIZE;
397 }
Chenbo Feng1f20ad32018-11-26 15:18:46 -0800398 size_t strTabOffset = ((Elf64_Shdr*)namesSection.base())->sh_offset;
399 size_t strTabSize = ((Elf64_Shdr*)namesSection.base())->sh_size;
Chenbo Feng75b410b2018-10-10 15:01:19 -0700400
401 Slice strTab = take(drop(fileContents, strTabOffset), strTabSize);
402 if (strTab.size() < strTabSize) {
403 ALOGE("string table out of bound\n");
404 return -EMSGSIZE;
405 }
406
407 for (int i = 0; i < elf->e_shnum; i++) {
408 Slice section = take(drop(sections, i * sizeof(Elf64_Shdr)), sizeof(Elf64_Shdr));
409 if (section.size() < sizeof(Elf64_Shdr)) {
410 ALOGE("section %d is out of bound, section size: %zu, header size: %zu, total size: "
411 "%zu",
412 i, section.size(), sizeof(Elf64_Shdr), sections.size());
413 return -EBADF;
414 }
Chenbo Feng1f20ad32018-11-26 15:18:46 -0800415 Elf64_Shdr* sectionPtr = (Elf64_Shdr*)section.base();
Chenbo Feng75b410b2018-10-10 15:01:19 -0700416 Slice nameSlice = drop(strTab, sectionPtr->sh_name);
417 if (nameSlice.size() == 0) {
418 ALOGE("nameSlice out of bound, i: %d, strTabSize: %zu, sh_name: %u", i, strTabSize,
419 sectionPtr->sh_name);
420 return -EBADF;
421 }
422 for (size_t i = 0; i < size; i++) {
423 BpfProgInfo* prog = programs + i;
Chenbo Feng1f20ad32018-11-26 15:18:46 -0800424 if (!strcmp((char*)nameSlice.base(), prog->name)) {
Chenbo Feng75b410b2018-10-10 15:01:19 -0700425 int ret = extractAndLoadProg(prog, sectionPtr, fileContents, mapPatterns);
426 if (ret) return ret;
427 }
428 }
429 }
430
431 // Check all the program struct passed in to make sure they all have a valid fd.
432 for (size_t i = 0; i < size; i++) {
433 BpfProgInfo* prog = programs + i;
434 if (access(prog->path, R_OK) == -1) {
435 ALOGE("Load program %s failed", prog->name);
436 return -EINVAL;
437 }
438 }
439 return 0;
440}
441
442int parseProgramsFromFile(const char* path, BpfProgInfo* programs, size_t size,
443 const std::vector<BpfMapInfo>& mapPatterns) {
Chenbo Feng4c9e9ec2018-10-16 20:31:52 -0700444 unique_fd fd(open(path, O_RDONLY | O_CLOEXEC));
Chenbo Feng75b410b2018-10-10 15:01:19 -0700445 int ret;
446 if (fd < 0) {
447 ret = -errno;
448 ALOGE("Failed to open %s program: %s", path, strerror(errno));
449 return ret;
450 }
451
452 struct stat stat;
453 if (fstat(fd.get(), &stat)) {
454 ret = -errno;
455 ALOGE("Failed to get file (%s) size: %s", path, strerror(errno));
456 return ret;
457 }
458
459 off_t fileLen = stat.st_size;
Chenbo Feng1f20ad32018-11-26 15:18:46 -0800460 char* baseAddr = (char*)mmap(NULL, fileLen, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd.get(), 0);
Chenbo Feng75b410b2018-10-10 15:01:19 -0700461 if (baseAddr == MAP_FAILED) {
462 ALOGE("Failed to map the program (%s) into memory: %s", path, strerror(errno));
463 ret = -errno;
464 return ret;
465 }
466
467 ret = parsePrograms(Slice(baseAddr, fileLen), programs, size, mapPatterns);
468
469 munmap(baseAddr, fileLen);
470 return ret;
471}
472
473} // namespace bpf
474} // namespace android