blob: c3a20849d80954fbaf0166d1bdb3e73f90d1f84a [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 Feng36575d02018-02-05 15:19:15 -080017#include <arpa/inet.h>
18#include <elf.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080019#include <error.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080020#include <fcntl.h>
21#include <inttypes.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080022#include <linux/bpf.h>
23#include <linux/unistd.h>
24#include <net/if.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080025#include <stdint.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080026#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080029#include <unistd.h>
30
31#include <sys/mman.h>
32#include <sys/socket.h>
33#include <sys/stat.h>
34#include <sys/types.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080035
36#include <android-base/stringprintf.h>
37#include <android-base/unique_fd.h>
38
39#include <netdutils/Misc.h>
Chenbo Feng36575d02018-02-05 15:19:15 -080040#include <netdutils/Slice.h>
Chenbo Feng05393d82018-01-09 15:18:43 -080041#include "bpf/BpfUtils.h"
42
Chenbo Feng36575d02018-02-05 15:19:15 -080043#include "bpf_shared.h"
44
Chenbo Feng05393d82018-01-09 15:18:43 -080045using android::base::unique_fd;
Chenbo Feng36575d02018-02-05 15:19:15 -080046using android::netdutils::Slice;
47
Chenbo Fengbd74cf52018-03-08 18:09:41 +090048#define BPF_PROG_PATH "/system/etc/bpf"
49
50#define INGRESS_PROG BPF_PROG_PATH"/cgroup_bpf_ingress_prog.o"
51#define EGRESS_PROG BPF_PROG_PATH"/cgroup_bpf_egress_prog.o"
Chenbo Feng36575d02018-02-05 15:19:15 -080052#define MAP_LD_CMD_HEAD 0x18
53
54#define FAIL(str) \
55 do { \
56 perror((str)); \
57 return -1; \
58 } while (0)
59
60// The BPF instruction bytes that we need to replace. x is a placeholder (e.g., COOKIE_TAG_MAP).
61#define MAP_SEARCH_PATTERN(x) \
62 { \
63 0x18, 0x01, 0x00, 0x00, \
64 (x)[0], (x)[1], (x)[2], (x)[3], \
65 0x00, 0x00, 0x00, 0x00, \
66 (x)[4], (x)[5], (x)[6], (x)[7] \
67 }
68
69// The bytes we'll replace them with. x is the actual fd number for the map at runtime.
70// The second byte is changed from 0x01 to 0x11 since 0x11 is the special command used
71// for bpf map fd loading. The original 0x01 is only a normal load command.
72#define MAP_REPLACE_PATTERN(x) \
73 { \
74 0x18, 0x11, 0x00, 0x00, \
75 (x)[0], (x)[1], (x)[2], (x)[3], \
76 0x00, 0x00, 0x00, 0x00, \
77 (x)[4], (x)[5], (x)[6], (x)[7] \
78 }
79
80#define MAP_CMD_SIZE 16
81#define LOG_BUF_SIZE 65536
Chenbo Feng05393d82018-01-09 15:18:43 -080082
83namespace android {
84namespace bpf {
85
Chenbo Feng36575d02018-02-05 15:19:15 -080086void makeFdReplacePattern(uint64_t code, uint64_t mapFd, char* pattern, char* cmd) {
87 char mapCode[sizeof(uint64_t)];
88 char mapCmd[sizeof(uint64_t)];
89 // The byte order is little endian for arm devices.
90 for (uint32_t i = 0; i < sizeof(uint64_t); i++) {
91 mapCode[i] = (code >> (i * 8)) & 0xFF;
92 mapCmd[i] = (mapFd >> (i * 8)) & 0xFF;
93 }
94
95 char tmpPattern[] = MAP_SEARCH_PATTERN(mapCode);
96 memcpy(pattern, tmpPattern, MAP_CMD_SIZE);
97 char tmpCmd[] = MAP_REPLACE_PATTERN(mapCmd);
98 memcpy(cmd, tmpCmd, MAP_CMD_SIZE);
99}
100
101int loadProg(const char* path, int cookieTagMap, int uidStatsMap, int tagStatsMap,
102 int uidCounterSetMap) {
103 int fd = open(path, O_RDONLY);
104 if (fd == -1) {
105 fprintf(stderr, "Failed to open %s program: %s", path, strerror(errno));
106 return -1;
107 }
108
109 struct stat stat;
110 if (fstat(fd, &stat)) FAIL("Fail to get file size");
111
112 off_t fileLen = stat.st_size;
113 char* baseAddr = (char*)mmap(NULL, fileLen, PROT_READ, MAP_PRIVATE, fd, 0);
114 if (baseAddr == MAP_FAILED) FAIL("Failed to map the program into memory");
115
116 if ((uint32_t)fileLen < sizeof(Elf64_Ehdr)) FAIL("file size too small for Elf64_Ehdr");
117
118 Elf64_Ehdr* elf = (Elf64_Ehdr*)baseAddr;
119
120 // Find section names string table. This is the section whose index is e_shstrndx.
121 if (elf->e_shstrndx == SHN_UNDEF ||
122 elf->e_shoff + (elf->e_shstrndx + 1) * sizeof(Elf64_Shdr) > (uint32_t)fileLen) {
123 FAIL("cannot locate namesSection\n");
124 }
125
126 Elf64_Shdr* sections = (Elf64_Shdr*)(baseAddr + elf->e_shoff);
127
128 Elf64_Shdr* namesSection = sections + elf->e_shstrndx;
129
130 if (namesSection->sh_offset + namesSection->sh_size > (uint32_t)fileLen)
131 FAIL("namesSection out of bound\n");
132
133 const char* strTab = baseAddr + namesSection->sh_offset;
134 void* progSection = nullptr;
135 uint64_t progSize = 0;
136 for (int i = 0; i < elf->e_shnum; i++) {
137 Elf64_Shdr* section = sections + i;
138 if (((char*)section - baseAddr) + sizeof(Elf64_Shdr) > (uint32_t)fileLen) {
139 FAIL("next section is out of bound\n");
140 }
141
142 if (!strcmp(strTab + section->sh_name, BPF_PROG_SEC_NAME)) {
143 progSection = baseAddr + section->sh_offset;
144 progSize = (uint64_t)section->sh_size;
145 break;
146 }
147 }
148
149 if (!progSection) FAIL("program section not found");
150 if ((char*)progSection - baseAddr + progSize > (uint32_t)fileLen)
151 FAIL("programSection out of bound\n");
152
153 char* prog = new char[progSize]();
154 memcpy(prog, progSection, progSize);
155
156 char cookieTagMapFdPattern[MAP_CMD_SIZE];
157 char cookieTagMapFdLoadByte[MAP_CMD_SIZE];
158 makeFdReplacePattern(COOKIE_TAG_MAP, cookieTagMap, cookieTagMapFdPattern,
159 cookieTagMapFdLoadByte);
160
161 char uidCounterSetMapFdPattern[MAP_CMD_SIZE];
162 char uidCounterSetMapFdLoadByte[MAP_CMD_SIZE];
163 makeFdReplacePattern(UID_COUNTERSET_MAP, uidCounterSetMap, uidCounterSetMapFdPattern,
164 uidCounterSetMapFdLoadByte);
165
166 char tagStatsMapFdPattern[MAP_CMD_SIZE];
167 char tagStatsMapFdLoadByte[MAP_CMD_SIZE];
168 makeFdReplacePattern(TAG_STATS_MAP, tagStatsMap, tagStatsMapFdPattern, tagStatsMapFdLoadByte);
169
170 char uidStatsMapFdPattern[MAP_CMD_SIZE];
171 char uidStatsMapFdLoadByte[MAP_CMD_SIZE];
172 makeFdReplacePattern(UID_STATS_MAP, uidStatsMap, uidStatsMapFdPattern, uidStatsMapFdLoadByte);
173
174 char* mapHead = prog;
175 while ((uint64_t)(mapHead - prog + MAP_CMD_SIZE) < progSize) {
176 // Scan the program, examining all possible places that might be the start of a map load
177 // operation (i.e., all byes of value MAP_LD_CMD_HEAD).
178 //
179 // In each of these places, check whether it is the start of one of the patterns we want to
180 // replace, and if so, replace it.
181 mapHead = (char*)memchr(mapHead, MAP_LD_CMD_HEAD, progSize);
182 if (!mapHead) break;
183 if ((uint64_t)(mapHead - prog + MAP_CMD_SIZE) < progSize) {
184 if (!memcmp(mapHead, cookieTagMapFdPattern, MAP_CMD_SIZE)) {
185 memcpy(mapHead, cookieTagMapFdLoadByte, MAP_CMD_SIZE);
186 mapHead += MAP_CMD_SIZE;
187 } else if (!memcmp(mapHead, uidCounterSetMapFdPattern, MAP_CMD_SIZE)) {
188 memcpy(mapHead, uidCounterSetMapFdLoadByte, MAP_CMD_SIZE);
189 mapHead += MAP_CMD_SIZE;
190 } else if (!memcmp(mapHead, tagStatsMapFdPattern, MAP_CMD_SIZE)) {
191 memcpy(mapHead, tagStatsMapFdLoadByte, MAP_CMD_SIZE);
192 mapHead += MAP_CMD_SIZE;
193 } else if (!memcmp(mapHead, uidStatsMapFdPattern, MAP_CMD_SIZE)) {
194 memcpy(mapHead, uidStatsMapFdLoadByte, MAP_CMD_SIZE);
195 mapHead += MAP_CMD_SIZE;
196 }
197 }
198 mapHead++;
199 }
200 Slice insns = Slice(prog, progSize);
201 char bpf_log_buf[LOG_BUF_SIZE];
202 Slice bpfLog = Slice(bpf_log_buf, sizeof(bpf_log_buf));
203 return bpfProgLoad(BPF_PROG_TYPE_CGROUP_SKB, insns, "Apache 2.0", 0, bpfLog);
204}
205
Chenbo Feng05393d82018-01-09 15:18:43 -0800206int loadAndAttachProgram(bpf_attach_type type, const char* path, const char* name,
207 const unique_fd& cookieTagMap, const unique_fd& uidCounterSetMap,
208 const unique_fd& uidStatsMap, const unique_fd& tagStatsMap) {
209 unique_fd cg_fd(open(CGROUP_ROOT_PATH, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
210 if (cg_fd < 0) {
211 perror("Failed to open the cgroup directory");
212 return -1;
213 }
214
215 unique_fd fd;
216 if (type == BPF_CGROUP_INET_EGRESS) {
Chenbo Feng36575d02018-02-05 15:19:15 -0800217 fd.reset(loadProg(INGRESS_PROG, cookieTagMap.get(), uidStatsMap.get(), tagStatsMap.get(),
218 uidCounterSetMap.get()));
Chenbo Feng05393d82018-01-09 15:18:43 -0800219 } else {
Chenbo Feng36575d02018-02-05 15:19:15 -0800220 fd.reset(loadProg(EGRESS_PROG, cookieTagMap.get(), uidStatsMap.get(), tagStatsMap.get(),
221 uidCounterSetMap.get()));
Chenbo Feng05393d82018-01-09 15:18:43 -0800222 }
223
224 if (fd < 0) {
225 fprintf(stderr, "load %s failed: %s", name, strerror(errno));
226 return -1;
227 }
228
229 int ret = attachProgram(type, fd, cg_fd);
230 if (ret) {
231 fprintf(stderr, "%s attach failed: %s", name, strerror(errno));
232 return -1;
233 }
234
235 ret = mapPin(fd, path);
236 if (ret) {
237 fprintf(stderr, "Pin %s as file %s failed: %s", name, path, strerror(errno));
238 return -1;
239 }
240 return 0;
241}
242
243} // namespace bpf
244} // namespace android
245
246using android::bpf::BPF_EGRESS_PROG_PATH;
247using android::bpf::BPF_INGRESS_PROG_PATH;
248using android::bpf::COOKIE_UID_MAP_PATH;
249using android::bpf::TAG_STATS_MAP_PATH;
250using android::bpf::UID_COUNTERSET_MAP_PATH;
251using android::bpf::UID_STATS_MAP_PATH;
252
253static void usage(void) {
254 fprintf(stderr,
255 "Usage: ./bpfloader [-i] [-e]\n"
256 " -i load ingress bpf program\n"
257 " -e load egress bpf program\n");
258}
259
260int main(int argc, char** argv) {
261 int ret = 0;
262 unique_fd cookieTagMap(android::bpf::mapRetrieve(COOKIE_UID_MAP_PATH, 0));
263 if (cookieTagMap < 0) {
264 perror("Failed to get cookieTagMap");
265 exit(-1);
266 }
267
268 unique_fd uidCounterSetMap(android::bpf::mapRetrieve(UID_COUNTERSET_MAP_PATH, 0));
269 if (uidCounterSetMap < 0) {
270 perror("Failed to get uidCounterSetMap");
271 exit(-1);
272 }
273
274 unique_fd uidStatsMap(android::bpf::mapRetrieve(UID_STATS_MAP_PATH, 0));
275 if (uidStatsMap < 0) {
276 perror("Failed to get uidStatsMap");
277 exit(-1);
278 }
279
280 unique_fd tagStatsMap(android::bpf::mapRetrieve(TAG_STATS_MAP_PATH, 0));
281 if (tagStatsMap < 0) {
282 perror("Failed to get tagStatsMap");
283 exit(-1);
284 }
285 int opt;
286 bool doIngress = false, doEgress = false;
287 while ((opt = getopt(argc, argv, "ie")) != -1) {
288 switch (opt) {
289 case 'i':
290 doIngress = true;
291 break;
292 case 'e':
293 doEgress = true;
294 break;
295 default:
296 fprintf(stderr, "unknown argument %c", opt);
297 usage();
298 exit(-1);
299 }
300 }
301 if (doIngress) {
302 ret = android::bpf::loadAndAttachProgram(BPF_CGROUP_INET_INGRESS, BPF_INGRESS_PROG_PATH,
303 "ingress_prog", cookieTagMap, uidCounterSetMap,
304 uidStatsMap, tagStatsMap);
305 if (ret) {
306 fprintf(stderr, "Failed to set up ingress program");
307 return ret;
308 }
309 }
310 if (doEgress) {
311 ret = android::bpf::loadAndAttachProgram(BPF_CGROUP_INET_EGRESS, BPF_EGRESS_PROG_PATH,
312 "egress_prog", cookieTagMap, uidCounterSetMap,
313 uidStatsMap, tagStatsMap);
314 if (ret) {
315 fprintf(stderr, "Failed to set up ingress program");
316 return ret;
317 }
318 }
319 return ret;
320}