blob: 7cb46162f8b971992a8f2a11d1429b583c5263b9 [file] [log] [blame]
Chenbo Feng05393d82018-01-09 15:18:43 -08001/*
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 Feng5ed17992018-03-13 21:30:49 -070017#ifndef LOG_TAG
18#define LOG_TAG "bpfloader"
19#endif
20
Chenbo Feng36575d02018-02-05 15:19:15 -080021#include <arpa/inet.h>
22#include <elf.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080023#include <error.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080024#include <fcntl.h>
25#include <inttypes.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080026#include <linux/bpf.h>
27#include <linux/unistd.h>
28#include <net/if.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080029#include <stdint.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080030#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080033#include <unistd.h>
34
35#include <sys/mman.h>
36#include <sys/socket.h>
37#include <sys/stat.h>
38#include <sys/types.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080039
40#include <android-base/stringprintf.h>
41#include <android-base/unique_fd.h>
Chenbo Feng5ed17992018-03-13 21:30:49 -070042#include <cutils/log.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080043
44#include <netdutils/Misc.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080045#include <netdutils/Slice.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080046#include "bpf/BpfUtils.h"
47
Chenbo Feng36575d02018-02-05 15:19:15 -080048#include "bpf_shared.h"
49
Chenbo Feng05393d82018-01-09 15:18:43 -080050using android::base::unique_fd;
Chenbo Feng36575d02018-02-05 15:19:15 -080051using android::netdutils::Slice;
52
Chenbo Fengbd74cf52018-03-08 18:09:41 +090053#define BPF_PROG_PATH "/system/etc/bpf"
54
55#define INGRESS_PROG BPF_PROG_PATH"/cgroup_bpf_ingress_prog.o"
56#define EGRESS_PROG BPF_PROG_PATH"/cgroup_bpf_egress_prog.o"
Chenbo Feng5ed17992018-03-13 21:30:49 -070057#define XT_BPF_INGRESS_PROG BPF_PROG_PATH "/xt_bpf_ingress_prog.o"
58#define XT_BPF_EGRESS_PROG BPF_PROG_PATH "/xt_bpf_egress_prog.o"
Chenbo Feng36575d02018-02-05 15:19:15 -080059#define MAP_LD_CMD_HEAD 0x18
60
Chenbo Feng5ed17992018-03-13 21:30:49 -070061#define FAIL(...) \
Chenbo Feng36575d02018-02-05 15:19:15 -080062 do { \
Chenbo Feng5ed17992018-03-13 21:30:49 -070063 ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)); \
64 exit(-1); \
Chenbo Feng36575d02018-02-05 15:19:15 -080065 } while (0)
66
67// The BPF instruction bytes that we need to replace. x is a placeholder (e.g., COOKIE_TAG_MAP).
68#define MAP_SEARCH_PATTERN(x) \
69 { \
70 0x18, 0x01, 0x00, 0x00, \
71 (x)[0], (x)[1], (x)[2], (x)[3], \
72 0x00, 0x00, 0x00, 0x00, \
73 (x)[4], (x)[5], (x)[6], (x)[7] \
74 }
75
76// The bytes we'll replace them with. x is the actual fd number for the map at runtime.
77// The second byte is changed from 0x01 to 0x11 since 0x11 is the special command used
78// for bpf map fd loading. The original 0x01 is only a normal load command.
79#define MAP_REPLACE_PATTERN(x) \
80 { \
81 0x18, 0x11, 0x00, 0x00, \
82 (x)[0], (x)[1], (x)[2], (x)[3], \
83 0x00, 0x00, 0x00, 0x00, \
84 (x)[4], (x)[5], (x)[6], (x)[7] \
85 }
86
87#define MAP_CMD_SIZE 16
88#define LOG_BUF_SIZE 65536
Chenbo Feng05393d82018-01-09 15:18:43 -080089
90namespace android {
91namespace bpf {
92
Chenbo Feng36575d02018-02-05 15:19:15 -080093void makeFdReplacePattern(uint64_t code, uint64_t mapFd, char* pattern, char* cmd) {
94 char mapCode[sizeof(uint64_t)];
95 char mapCmd[sizeof(uint64_t)];
96 // The byte order is little endian for arm devices.
97 for (uint32_t i = 0; i < sizeof(uint64_t); i++) {
98 mapCode[i] = (code >> (i * 8)) & 0xFF;
99 mapCmd[i] = (mapFd >> (i * 8)) & 0xFF;
100 }
101
102 char tmpPattern[] = MAP_SEARCH_PATTERN(mapCode);
103 memcpy(pattern, tmpPattern, MAP_CMD_SIZE);
104 char tmpCmd[] = MAP_REPLACE_PATTERN(mapCmd);
105 memcpy(cmd, tmpCmd, MAP_CMD_SIZE);
106}
107
108int loadProg(const char* path, int cookieTagMap, int uidStatsMap, int tagStatsMap,
Chenbo Feng5ed17992018-03-13 21:30:49 -0700109 int uidCounterSetMap, int ifaceStatsMap) {
Chenbo Feng36575d02018-02-05 15:19:15 -0800110 int fd = open(path, O_RDONLY);
111 if (fd == -1) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700112 FAIL("Failed to open %s program: %s", path, strerror(errno));
Chenbo Feng36575d02018-02-05 15:19:15 -0800113 }
114
115 struct stat stat;
116 if (fstat(fd, &stat)) FAIL("Fail to get file size");
117
118 off_t fileLen = stat.st_size;
119 char* baseAddr = (char*)mmap(NULL, fileLen, PROT_READ, MAP_PRIVATE, fd, 0);
120 if (baseAddr == MAP_FAILED) FAIL("Failed to map the program into memory");
121
122 if ((uint32_t)fileLen < sizeof(Elf64_Ehdr)) FAIL("file size too small for Elf64_Ehdr");
123
124 Elf64_Ehdr* elf = (Elf64_Ehdr*)baseAddr;
125
126 // Find section names string table. This is the section whose index is e_shstrndx.
127 if (elf->e_shstrndx == SHN_UNDEF ||
128 elf->e_shoff + (elf->e_shstrndx + 1) * sizeof(Elf64_Shdr) > (uint32_t)fileLen) {
129 FAIL("cannot locate namesSection\n");
130 }
131
132 Elf64_Shdr* sections = (Elf64_Shdr*)(baseAddr + elf->e_shoff);
133
134 Elf64_Shdr* namesSection = sections + elf->e_shstrndx;
135
136 if (namesSection->sh_offset + namesSection->sh_size > (uint32_t)fileLen)
137 FAIL("namesSection out of bound\n");
138
139 const char* strTab = baseAddr + namesSection->sh_offset;
140 void* progSection = nullptr;
141 uint64_t progSize = 0;
142 for (int i = 0; i < elf->e_shnum; i++) {
143 Elf64_Shdr* section = sections + i;
144 if (((char*)section - baseAddr) + sizeof(Elf64_Shdr) > (uint32_t)fileLen) {
145 FAIL("next section is out of bound\n");
146 }
147
148 if (!strcmp(strTab + section->sh_name, BPF_PROG_SEC_NAME)) {
149 progSection = baseAddr + section->sh_offset;
150 progSize = (uint64_t)section->sh_size;
151 break;
152 }
153 }
154
155 if (!progSection) FAIL("program section not found");
156 if ((char*)progSection - baseAddr + progSize > (uint32_t)fileLen)
157 FAIL("programSection out of bound\n");
158
159 char* prog = new char[progSize]();
160 memcpy(prog, progSection, progSize);
161
162 char cookieTagMapFdPattern[MAP_CMD_SIZE];
163 char cookieTagMapFdLoadByte[MAP_CMD_SIZE];
164 makeFdReplacePattern(COOKIE_TAG_MAP, cookieTagMap, cookieTagMapFdPattern,
165 cookieTagMapFdLoadByte);
166
167 char uidCounterSetMapFdPattern[MAP_CMD_SIZE];
168 char uidCounterSetMapFdLoadByte[MAP_CMD_SIZE];
169 makeFdReplacePattern(UID_COUNTERSET_MAP, uidCounterSetMap, uidCounterSetMapFdPattern,
170 uidCounterSetMapFdLoadByte);
171
172 char tagStatsMapFdPattern[MAP_CMD_SIZE];
173 char tagStatsMapFdLoadByte[MAP_CMD_SIZE];
174 makeFdReplacePattern(TAG_STATS_MAP, tagStatsMap, tagStatsMapFdPattern, tagStatsMapFdLoadByte);
175
176 char uidStatsMapFdPattern[MAP_CMD_SIZE];
177 char uidStatsMapFdLoadByte[MAP_CMD_SIZE];
178 makeFdReplacePattern(UID_STATS_MAP, uidStatsMap, uidStatsMapFdPattern, uidStatsMapFdLoadByte);
179
Chenbo Feng5ed17992018-03-13 21:30:49 -0700180 char ifaceStatsMapFdPattern[MAP_CMD_SIZE];
181 char ifaceStatsMapFdLoadByte[MAP_CMD_SIZE];
182 makeFdReplacePattern(IFACE_STATS_MAP, ifaceStatsMap, ifaceStatsMapFdPattern,
183 ifaceStatsMapFdLoadByte);
184
Chenbo Feng36575d02018-02-05 15:19:15 -0800185 char* mapHead = prog;
186 while ((uint64_t)(mapHead - prog + MAP_CMD_SIZE) < progSize) {
187 // Scan the program, examining all possible places that might be the start of a map load
188 // operation (i.e., all byes of value MAP_LD_CMD_HEAD).
189 //
190 // In each of these places, check whether it is the start of one of the patterns we want to
191 // replace, and if so, replace it.
192 mapHead = (char*)memchr(mapHead, MAP_LD_CMD_HEAD, progSize);
193 if (!mapHead) break;
194 if ((uint64_t)(mapHead - prog + MAP_CMD_SIZE) < progSize) {
195 if (!memcmp(mapHead, cookieTagMapFdPattern, MAP_CMD_SIZE)) {
196 memcpy(mapHead, cookieTagMapFdLoadByte, MAP_CMD_SIZE);
197 mapHead += MAP_CMD_SIZE;
198 } else if (!memcmp(mapHead, uidCounterSetMapFdPattern, MAP_CMD_SIZE)) {
199 memcpy(mapHead, uidCounterSetMapFdLoadByte, MAP_CMD_SIZE);
200 mapHead += MAP_CMD_SIZE;
201 } else if (!memcmp(mapHead, tagStatsMapFdPattern, MAP_CMD_SIZE)) {
202 memcpy(mapHead, tagStatsMapFdLoadByte, MAP_CMD_SIZE);
203 mapHead += MAP_CMD_SIZE;
204 } else if (!memcmp(mapHead, uidStatsMapFdPattern, MAP_CMD_SIZE)) {
205 memcpy(mapHead, uidStatsMapFdLoadByte, MAP_CMD_SIZE);
206 mapHead += MAP_CMD_SIZE;
Chenbo Feng5ed17992018-03-13 21:30:49 -0700207 } else if (!memcmp(mapHead, ifaceStatsMapFdPattern, MAP_CMD_SIZE)) {
208 memcpy(mapHead, ifaceStatsMapFdLoadByte, MAP_CMD_SIZE);
209 mapHead += MAP_CMD_SIZE;
Chenbo Feng36575d02018-02-05 15:19:15 -0800210 }
211 }
212 mapHead++;
213 }
214 Slice insns = Slice(prog, progSize);
215 char bpf_log_buf[LOG_BUF_SIZE];
216 Slice bpfLog = Slice(bpf_log_buf, sizeof(bpf_log_buf));
Chenbo Feng5ed17992018-03-13 21:30:49 -0700217 if (strcmp(path, XT_BPF_INGRESS_PROG) && strcmp(path, XT_BPF_EGRESS_PROG)) {
218 return bpfProgLoad(BPF_PROG_TYPE_CGROUP_SKB, insns, "Apache 2.0", 0, bpfLog);
219 }
220 return bpfProgLoad(BPF_PROG_TYPE_SOCKET_FILTER, insns, "Apache 2.0", 0, bpfLog);
Chenbo Feng36575d02018-02-05 15:19:15 -0800221}
222
Chenbo Feng05393d82018-01-09 15:18:43 -0800223int loadAndAttachProgram(bpf_attach_type type, const char* path, const char* name,
224 const unique_fd& cookieTagMap, const unique_fd& uidCounterSetMap,
Chenbo Feng5ed17992018-03-13 21:30:49 -0700225 const unique_fd& uidStatsMap, const unique_fd& tagStatsMap,
226 const unique_fd& ifaceStatsMap) {
Chenbo Feng05393d82018-01-09 15:18:43 -0800227 unique_fd cg_fd(open(CGROUP_ROOT_PATH, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
228 if (cg_fd < 0) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700229 FAIL("Failed to open the cgroup directory");
Chenbo Feng05393d82018-01-09 15:18:43 -0800230 }
231
232 unique_fd fd;
233 if (type == BPF_CGROUP_INET_EGRESS) {
Chenbo Feng36575d02018-02-05 15:19:15 -0800234 fd.reset(loadProg(INGRESS_PROG, cookieTagMap.get(), uidStatsMap.get(), tagStatsMap.get(),
Chenbo Feng5ed17992018-03-13 21:30:49 -0700235 uidCounterSetMap.get(), ifaceStatsMap.get()));
236 } else if (type == BPF_CGROUP_INET_INGRESS) {
Chenbo Feng36575d02018-02-05 15:19:15 -0800237 fd.reset(loadProg(EGRESS_PROG, cookieTagMap.get(), uidStatsMap.get(), tagStatsMap.get(),
Chenbo Feng5ed17992018-03-13 21:30:49 -0700238 uidCounterSetMap.get(), ifaceStatsMap.get()));
239 } else if (!strcmp(name, "xt_bpf_ingress_prog")) {
240 fd.reset(loadProg(XT_BPF_INGRESS_PROG, cookieTagMap.get(), uidStatsMap.get(),
241 tagStatsMap.get(), uidCounterSetMap.get(), ifaceStatsMap.get()));
242 } else if (!strcmp(name, "xt_bpf_egress_prog")) {
243 fd.reset(loadProg(XT_BPF_EGRESS_PROG, cookieTagMap.get(), uidStatsMap.get(),
244 tagStatsMap.get(), uidCounterSetMap.get(), ifaceStatsMap.get()));
245 } else {
246 FAIL("Unrecognized program type: %s", name);
Chenbo Feng05393d82018-01-09 15:18:43 -0800247 }
248
249 if (fd < 0) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700250 FAIL("load %s failed: %s", name, strerror(errno));
Chenbo Feng05393d82018-01-09 15:18:43 -0800251 }
Chenbo Feng5ed17992018-03-13 21:30:49 -0700252 int ret = 0;
253 if (type == BPF_CGROUP_INET_EGRESS || type == BPF_CGROUP_INET_INGRESS) {
254 ret = attachProgram(type, fd, cg_fd);
255 if (ret) {
256 FAIL("%s attach failed: %s", name, strerror(errno));
257 }
Chenbo Feng05393d82018-01-09 15:18:43 -0800258 }
259
260 ret = mapPin(fd, path);
261 if (ret) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700262 FAIL("Pin %s as file %s failed: %s", name, path, strerror(errno));
Chenbo Feng05393d82018-01-09 15:18:43 -0800263 }
264 return 0;
265}
266
267} // namespace bpf
268} // namespace android
269
270using android::bpf::BPF_EGRESS_PROG_PATH;
271using android::bpf::BPF_INGRESS_PROG_PATH;
272using android::bpf::COOKIE_UID_MAP_PATH;
Chenbo Feng5ed17992018-03-13 21:30:49 -0700273using android::bpf::IFACE_STATS_MAP_PATH;
Chenbo Feng05393d82018-01-09 15:18:43 -0800274using android::bpf::TAG_STATS_MAP_PATH;
275using android::bpf::UID_COUNTERSET_MAP_PATH;
276using android::bpf::UID_STATS_MAP_PATH;
Chenbo Feng5ed17992018-03-13 21:30:49 -0700277using android::bpf::XT_BPF_EGRESS_PROG_PATH;
278using android::bpf::XT_BPF_INGRESS_PROG_PATH;
Chenbo Feng05393d82018-01-09 15:18:43 -0800279
280static void usage(void) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700281 ALOGE( "Usage: ./bpfloader [-i] [-e]\n"
282 " -i load ingress bpf program\n"
283 " -e load egress bpf program\n"
284 " -p load prerouting xt_bpf program\n"
285 " -m load mangle xt_bpf program\n");
Chenbo Feng05393d82018-01-09 15:18:43 -0800286}
287
288int main(int argc, char** argv) {
289 int ret = 0;
290 unique_fd cookieTagMap(android::bpf::mapRetrieve(COOKIE_UID_MAP_PATH, 0));
291 if (cookieTagMap < 0) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700292 FAIL("Failed to get cookieTagMap");
Chenbo Feng05393d82018-01-09 15:18:43 -0800293 }
294
295 unique_fd uidCounterSetMap(android::bpf::mapRetrieve(UID_COUNTERSET_MAP_PATH, 0));
296 if (uidCounterSetMap < 0) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700297 FAIL("Failed to get uidCounterSetMap");
Chenbo Feng05393d82018-01-09 15:18:43 -0800298 }
299
300 unique_fd uidStatsMap(android::bpf::mapRetrieve(UID_STATS_MAP_PATH, 0));
301 if (uidStatsMap < 0) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700302 FAIL("Failed to get uidStatsMap");
Chenbo Feng05393d82018-01-09 15:18:43 -0800303 }
304
305 unique_fd tagStatsMap(android::bpf::mapRetrieve(TAG_STATS_MAP_PATH, 0));
306 if (tagStatsMap < 0) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700307 FAIL("Failed to get tagStatsMap");
Chenbo Feng05393d82018-01-09 15:18:43 -0800308 }
Chenbo Feng5ed17992018-03-13 21:30:49 -0700309
310 unique_fd ifaceStatsMap(android::bpf::mapRetrieve(IFACE_STATS_MAP_PATH, 0));
311 if (ifaceStatsMap < 0) {
312 FAIL("Failed to get ifaceStatsMap");
313 }
314
Chenbo Feng05393d82018-01-09 15:18:43 -0800315 int opt;
Chenbo Feng5ed17992018-03-13 21:30:49 -0700316 bool doIngress = false, doEgress = false, doPrerouting = false, doMangle = false;
317 while ((opt = getopt(argc, argv, "iepm")) != -1) {
Chenbo Feng05393d82018-01-09 15:18:43 -0800318 switch (opt) {
319 case 'i':
320 doIngress = true;
321 break;
322 case 'e':
323 doEgress = true;
324 break;
Chenbo Feng5ed17992018-03-13 21:30:49 -0700325 case 'p':
326 doPrerouting = true;
327 break;
328 case 'm':
329 doMangle = true;
330 break;
Chenbo Feng05393d82018-01-09 15:18:43 -0800331 default:
Chenbo Feng05393d82018-01-09 15:18:43 -0800332 usage();
Chenbo Feng5ed17992018-03-13 21:30:49 -0700333 FAIL("unknown argument %c", opt);
Chenbo Feng05393d82018-01-09 15:18:43 -0800334 }
335 }
336 if (doIngress) {
337 ret = android::bpf::loadAndAttachProgram(BPF_CGROUP_INET_INGRESS, BPF_INGRESS_PROG_PATH,
338 "ingress_prog", cookieTagMap, uidCounterSetMap,
Chenbo Feng5ed17992018-03-13 21:30:49 -0700339 uidStatsMap, tagStatsMap, ifaceStatsMap);
Chenbo Feng05393d82018-01-09 15:18:43 -0800340 if (ret) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700341 FAIL("Failed to set up ingress program");
Chenbo Feng05393d82018-01-09 15:18:43 -0800342 }
343 }
344 if (doEgress) {
345 ret = android::bpf::loadAndAttachProgram(BPF_CGROUP_INET_EGRESS, BPF_EGRESS_PROG_PATH,
346 "egress_prog", cookieTagMap, uidCounterSetMap,
Chenbo Feng5ed17992018-03-13 21:30:49 -0700347 uidStatsMap, tagStatsMap, ifaceStatsMap);
Chenbo Feng05393d82018-01-09 15:18:43 -0800348 if (ret) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700349 FAIL("Failed to set up ingress program");
350 }
351 }
352 if (doPrerouting) {
353 ret = android::bpf::loadAndAttachProgram(
354 MAX_BPF_ATTACH_TYPE, XT_BPF_INGRESS_PROG_PATH, "xt_bpf_ingress_prog", cookieTagMap,
355 uidCounterSetMap, uidStatsMap, tagStatsMap, ifaceStatsMap);
356 if (ret) {
357 FAIL("Failed to set up xt_bpf program");
358 }
359 }
360 if (doMangle) {
361 ret = android::bpf::loadAndAttachProgram(
362 MAX_BPF_ATTACH_TYPE, XT_BPF_EGRESS_PROG_PATH, "xt_bpf_egress_prog", cookieTagMap,
363 uidCounterSetMap, uidStatsMap, tagStatsMap, ifaceStatsMap);
364 if (ret) {
365 FAIL("Failed to set up xt_bpf program");
Chenbo Feng05393d82018-01-09 15:18:43 -0800366 }
367 }
368 return ret;
369}