]>
Commit | Line | Data |
---|---|---|
9b474ece SD |
1 | /* eBPF example program: |
2 | * | |
3 | * - Creates arraymap in kernel with 4 bytes keys and 8 byte values | |
4 | * | |
5 | * - Loads eBPF program | |
6 | * | |
7 | * The eBPF program accesses the map passed in to store two pieces of | |
8 | * information. The number of invocations of the program, which maps | |
9 | * to the number of packets received, is stored to key 0. Key 1 is | |
10 | * incremented on each iteration by the number of bytes stored in | |
28ba0687 RG |
11 | * the skb. The program also stores the number of received bytes |
12 | * in the cgroup storage. | |
9b474ece SD |
13 | * |
14 | * - Attaches the new program to a cgroup using BPF_PROG_ATTACH | |
15 | * | |
16 | * - Every second, reads map[0] and map[1] to see how many bytes and | |
17 | * packets were seen on any socket of tasks in the given cgroup. | |
18 | */ | |
19 | ||
20 | #define _GNU_SOURCE | |
21 | ||
22 | #include <stdio.h> | |
23 | #include <stdlib.h> | |
24 | #include <assert.h> | |
28ba0687 RG |
25 | #include <sys/resource.h> |
26 | #include <sys/time.h> | |
9b474ece SD |
27 | #include <unistd.h> |
28 | ||
29 | #include <linux/bpf.h> | |
8d930450 | 30 | #include <bpf/bpf.h> |
9b474ece | 31 | |
8d930450 | 32 | #include "bpf_insn.h" |
28ba0687 | 33 | #include "bpf_rlimit.h" |
9b474ece SD |
34 | #include "cgroup_helpers.h" |
35 | ||
36 | #define FOO "/foo" | |
37 | #define BAR "/foo/bar/" | |
39323e78 | 38 | #define PING_CMD "ping -c1 -w1 127.0.0.1 > /dev/null" |
9b474ece | 39 | |
d40fc181 JS |
40 | char bpf_log_buf[BPF_LOG_BUF_SIZE]; |
41 | ||
9b474ece SD |
42 | static int prog_load(int verdict) |
43 | { | |
44 | int ret; | |
45 | struct bpf_insn prog[] = { | |
46 | BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ | |
47 | BPF_EXIT_INSN(), | |
48 | }; | |
43371c83 | 49 | size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); |
9b474ece | 50 | |
d40fc181 | 51 | ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, |
43371c83 | 52 | prog, insns_cnt, "GPL", 0, |
d40fc181 | 53 | bpf_log_buf, BPF_LOG_BUF_SIZE); |
9b474ece SD |
54 | |
55 | if (ret < 0) { | |
56 | log_err("Loading program"); | |
57 | printf("Output from verifier:\n%s\n-------\n", bpf_log_buf); | |
58 | return 0; | |
59 | } | |
60 | return ret; | |
61 | } | |
62 | ||
39323e78 | 63 | static int test_foo_bar(void) |
9b474ece SD |
64 | { |
65 | int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0; | |
66 | ||
67 | allow_prog = prog_load(1); | |
68 | if (!allow_prog) | |
69 | goto err; | |
70 | ||
71 | drop_prog = prog_load(0); | |
72 | if (!drop_prog) | |
73 | goto err; | |
74 | ||
75 | if (setup_cgroup_environment()) | |
76 | goto err; | |
77 | ||
78 | /* Create cgroup /foo, get fd, and join it */ | |
79 | foo = create_and_get_cgroup(FOO); | |
80 | if (!foo) | |
81 | goto err; | |
82 | ||
83 | if (join_cgroup(FOO)) | |
84 | goto err; | |
85 | ||
51de0825 DA |
86 | if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, |
87 | BPF_F_ALLOW_OVERRIDE)) { | |
9b474ece SD |
88 | log_err("Attaching prog to /foo"); |
89 | goto err; | |
90 | } | |
91 | ||
7f677633 | 92 | printf("Attached DROP prog. This ping in cgroup /foo should fail...\n"); |
9b474ece SD |
93 | assert(system(PING_CMD) != 0); |
94 | ||
95 | /* Create cgroup /foo/bar, get fd, and join it */ | |
96 | bar = create_and_get_cgroup(BAR); | |
97 | if (!bar) | |
98 | goto err; | |
99 | ||
100 | if (join_cgroup(BAR)) | |
101 | goto err; | |
102 | ||
7f677633 | 103 | printf("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n"); |
9b474ece SD |
104 | assert(system(PING_CMD) != 0); |
105 | ||
51de0825 DA |
106 | if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, |
107 | BPF_F_ALLOW_OVERRIDE)) { | |
9b474ece SD |
108 | log_err("Attaching prog to /foo/bar"); |
109 | goto err; | |
110 | } | |
111 | ||
7f677633 | 112 | printf("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n"); |
9b474ece SD |
113 | assert(system(PING_CMD) == 0); |
114 | ||
9b474ece SD |
115 | if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { |
116 | log_err("Detaching program from /foo/bar"); | |
117 | goto err; | |
118 | } | |
119 | ||
7f677633 AS |
120 | printf("Detached PASS from /foo/bar while DROP is attached to /foo.\n" |
121 | "This ping in cgroup /foo/bar should fail...\n"); | |
9b474ece SD |
122 | assert(system(PING_CMD) != 0); |
123 | ||
51de0825 DA |
124 | if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, |
125 | BPF_F_ALLOW_OVERRIDE)) { | |
9b474ece SD |
126 | log_err("Attaching prog to /foo/bar"); |
127 | goto err; | |
128 | } | |
129 | ||
130 | if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) { | |
131 | log_err("Detaching program from /foo"); | |
132 | goto err; | |
133 | } | |
134 | ||
7f677633 AS |
135 | printf("Attached PASS from /foo/bar and detached DROP from /foo.\n" |
136 | "This ping in cgroup /foo/bar should pass...\n"); | |
9b474ece SD |
137 | assert(system(PING_CMD) == 0); |
138 | ||
51de0825 DA |
139 | if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, |
140 | BPF_F_ALLOW_OVERRIDE)) { | |
7f677633 AS |
141 | log_err("Attaching prog to /foo/bar"); |
142 | goto err; | |
143 | } | |
144 | ||
145 | if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) { | |
146 | errno = 0; | |
147 | log_err("Unexpected success attaching prog to /foo/bar"); | |
148 | goto err; | |
149 | } | |
150 | ||
151 | if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { | |
152 | log_err("Detaching program from /foo/bar"); | |
153 | goto err; | |
154 | } | |
155 | ||
156 | if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) { | |
157 | errno = 0; | |
158 | log_err("Unexpected success in double detach from /foo"); | |
159 | goto err; | |
160 | } | |
161 | ||
162 | if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) { | |
163 | log_err("Attaching non-overridable prog to /foo"); | |
164 | goto err; | |
165 | } | |
166 | ||
167 | if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) { | |
168 | errno = 0; | |
169 | log_err("Unexpected success attaching non-overridable prog to /foo/bar"); | |
170 | goto err; | |
171 | } | |
172 | ||
51de0825 DA |
173 | if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, |
174 | BPF_F_ALLOW_OVERRIDE)) { | |
7f677633 AS |
175 | errno = 0; |
176 | log_err("Unexpected success attaching overridable prog to /foo/bar"); | |
177 | goto err; | |
178 | } | |
179 | ||
51de0825 DA |
180 | if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, |
181 | BPF_F_ALLOW_OVERRIDE)) { | |
7f677633 AS |
182 | errno = 0; |
183 | log_err("Unexpected success attaching overridable prog to /foo"); | |
184 | goto err; | |
185 | } | |
186 | ||
187 | if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) { | |
188 | log_err("Attaching different non-overridable prog to /foo"); | |
189 | goto err; | |
190 | } | |
191 | ||
9b474ece SD |
192 | goto out; |
193 | ||
194 | err: | |
195 | rc = 1; | |
196 | ||
197 | out: | |
198 | close(foo); | |
199 | close(bar); | |
200 | cleanup_cgroup_environment(); | |
7f677633 | 201 | if (!rc) |
39323e78 AS |
202 | printf("### override:PASS\n"); |
203 | else | |
204 | printf("### override:FAIL\n"); | |
205 | return rc; | |
206 | } | |
207 | ||
208 | static int map_fd = -1; | |
209 | ||
210 | static int prog_load_cnt(int verdict, int val) | |
211 | { | |
5fcbd29b | 212 | int cgroup_storage_fd, percpu_cgroup_storage_fd; |
28ba0687 | 213 | |
39323e78 AS |
214 | if (map_fd < 0) |
215 | map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0); | |
216 | if (map_fd < 0) { | |
217 | printf("failed to create map '%s'\n", strerror(errno)); | |
218 | return -1; | |
219 | } | |
220 | ||
28ba0687 RG |
221 | cgroup_storage_fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE, |
222 | sizeof(struct bpf_cgroup_storage_key), 8, 0, 0); | |
223 | if (cgroup_storage_fd < 0) { | |
224 | printf("failed to create map '%s'\n", strerror(errno)); | |
225 | return -1; | |
226 | } | |
227 | ||
5fcbd29b RG |
228 | percpu_cgroup_storage_fd = bpf_create_map( |
229 | BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, | |
230 | sizeof(struct bpf_cgroup_storage_key), 8, 0, 0); | |
231 | if (percpu_cgroup_storage_fd < 0) { | |
232 | printf("failed to create map '%s'\n", strerror(errno)); | |
233 | return -1; | |
234 | } | |
235 | ||
39323e78 AS |
236 | struct bpf_insn prog[] = { |
237 | BPF_MOV32_IMM(BPF_REG_0, 0), | |
238 | BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ | |
239 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | |
240 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ | |
241 | BPF_LD_MAP_FD(BPF_REG_1, map_fd), | |
242 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), | |
243 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), | |
244 | BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */ | |
245 | BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */ | |
5fcbd29b | 246 | |
28ba0687 RG |
247 | BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd), |
248 | BPF_MOV64_IMM(BPF_REG_2, 0), | |
249 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage), | |
250 | BPF_MOV64_IMM(BPF_REG_1, val), | |
251 | BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0), | |
5fcbd29b RG |
252 | |
253 | BPF_LD_MAP_FD(BPF_REG_1, percpu_cgroup_storage_fd), | |
254 | BPF_MOV64_IMM(BPF_REG_2, 0), | |
255 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage), | |
256 | BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), | |
257 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0x1), | |
258 | BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0), | |
259 | ||
39323e78 AS |
260 | BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ |
261 | BPF_EXIT_INSN(), | |
262 | }; | |
263 | size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); | |
264 | int ret; | |
265 | ||
266 | ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, | |
267 | prog, insns_cnt, "GPL", 0, | |
268 | bpf_log_buf, BPF_LOG_BUF_SIZE); | |
269 | ||
270 | if (ret < 0) { | |
271 | log_err("Loading program"); | |
272 | printf("Output from verifier:\n%s\n-------\n", bpf_log_buf); | |
273 | return 0; | |
274 | } | |
28ba0687 | 275 | close(cgroup_storage_fd); |
39323e78 AS |
276 | return ret; |
277 | } | |
278 | ||
279 | ||
280 | static int test_multiprog(void) | |
281 | { | |
dfc06999 | 282 | __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id; |
39323e78 AS |
283 | int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0; |
284 | int drop_prog, allow_prog[6] = {}, rc = 0; | |
285 | unsigned long long value; | |
286 | int i = 0; | |
287 | ||
288 | for (i = 0; i < 6; i++) { | |
289 | allow_prog[i] = prog_load_cnt(1, 1 << i); | |
290 | if (!allow_prog[i]) | |
291 | goto err; | |
292 | } | |
293 | drop_prog = prog_load_cnt(0, 1); | |
294 | if (!drop_prog) | |
295 | goto err; | |
296 | ||
297 | if (setup_cgroup_environment()) | |
298 | goto err; | |
299 | ||
300 | cg1 = create_and_get_cgroup("/cg1"); | |
301 | if (!cg1) | |
302 | goto err; | |
303 | cg2 = create_and_get_cgroup("/cg1/cg2"); | |
304 | if (!cg2) | |
305 | goto err; | |
306 | cg3 = create_and_get_cgroup("/cg1/cg2/cg3"); | |
307 | if (!cg3) | |
308 | goto err; | |
309 | cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4"); | |
310 | if (!cg4) | |
311 | goto err; | |
312 | cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5"); | |
313 | if (!cg5) | |
314 | goto err; | |
315 | ||
316 | if (join_cgroup("/cg1/cg2/cg3/cg4/cg5")) | |
317 | goto err; | |
318 | ||
51de0825 DA |
319 | if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, |
320 | BPF_F_ALLOW_MULTI)) { | |
39323e78 AS |
321 | log_err("Attaching prog to cg1"); |
322 | goto err; | |
323 | } | |
51de0825 DA |
324 | if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, |
325 | BPF_F_ALLOW_MULTI)) { | |
39323e78 AS |
326 | log_err("Unexpected success attaching the same prog to cg1"); |
327 | goto err; | |
328 | } | |
51de0825 DA |
329 | if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS, |
330 | BPF_F_ALLOW_MULTI)) { | |
39323e78 AS |
331 | log_err("Attaching prog2 to cg1"); |
332 | goto err; | |
333 | } | |
51de0825 DA |
334 | if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS, |
335 | BPF_F_ALLOW_OVERRIDE)) { | |
39323e78 AS |
336 | log_err("Attaching prog to cg2"); |
337 | goto err; | |
338 | } | |
51de0825 DA |
339 | if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS, |
340 | BPF_F_ALLOW_MULTI)) { | |
39323e78 AS |
341 | log_err("Attaching prog to cg3"); |
342 | goto err; | |
343 | } | |
51de0825 DA |
344 | if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS, |
345 | BPF_F_ALLOW_OVERRIDE)) { | |
39323e78 AS |
346 | log_err("Attaching prog to cg4"); |
347 | goto err; | |
348 | } | |
349 | if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) { | |
350 | log_err("Attaching prog to cg5"); | |
351 | goto err; | |
352 | } | |
353 | assert(system(PING_CMD) == 0); | |
354 | assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); | |
355 | assert(value == 1 + 2 + 8 + 32); | |
356 | ||
dfc06999 AS |
357 | /* query the number of effective progs in cg5 */ |
358 | assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, | |
359 | NULL, NULL, &prog_cnt) == 0); | |
360 | assert(prog_cnt == 4); | |
361 | /* retrieve prog_ids of effective progs in cg5 */ | |
362 | assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, | |
363 | &attach_flags, prog_ids, &prog_cnt) == 0); | |
364 | assert(prog_cnt == 4); | |
365 | assert(attach_flags == 0); | |
366 | saved_prog_id = prog_ids[0]; | |
367 | /* check enospc handling */ | |
368 | prog_ids[0] = 0; | |
369 | prog_cnt = 2; | |
370 | assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, | |
371 | &attach_flags, prog_ids, &prog_cnt) == -1 && | |
372 | errno == ENOSPC); | |
373 | assert(prog_cnt == 4); | |
374 | /* check that prog_ids are returned even when buffer is too small */ | |
375 | assert(prog_ids[0] == saved_prog_id); | |
376 | /* retrieve prog_id of single attached prog in cg5 */ | |
377 | prog_ids[0] = 0; | |
378 | assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, | |
379 | NULL, prog_ids, &prog_cnt) == 0); | |
380 | assert(prog_cnt == 1); | |
381 | assert(prog_ids[0] == saved_prog_id); | |
382 | ||
39323e78 AS |
383 | /* detach bottom program and ping again */ |
384 | if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) { | |
385 | log_err("Detaching prog from cg5"); | |
386 | goto err; | |
387 | } | |
388 | value = 0; | |
389 | assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); | |
390 | assert(system(PING_CMD) == 0); | |
391 | assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); | |
392 | assert(value == 1 + 2 + 8 + 16); | |
393 | ||
394 | /* detach 3rd from bottom program and ping again */ | |
395 | errno = 0; | |
396 | if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) { | |
397 | log_err("Unexpected success on detach from cg3"); | |
398 | goto err; | |
399 | } | |
400 | if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) { | |
401 | log_err("Detaching from cg3"); | |
402 | goto err; | |
403 | } | |
404 | value = 0; | |
405 | assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); | |
406 | assert(system(PING_CMD) == 0); | |
407 | assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); | |
408 | assert(value == 1 + 2 + 16); | |
409 | ||
410 | /* detach 2nd from bottom program and ping again */ | |
411 | if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) { | |
412 | log_err("Detaching prog from cg4"); | |
413 | goto err; | |
414 | } | |
415 | value = 0; | |
416 | assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); | |
417 | assert(system(PING_CMD) == 0); | |
418 | assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); | |
419 | assert(value == 1 + 2 + 4); | |
dfc06999 AS |
420 | |
421 | prog_cnt = 4; | |
422 | assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, | |
423 | &attach_flags, prog_ids, &prog_cnt) == 0); | |
424 | assert(prog_cnt == 3); | |
425 | assert(attach_flags == 0); | |
426 | assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, | |
427 | NULL, prog_ids, &prog_cnt) == 0); | |
428 | assert(prog_cnt == 0); | |
39323e78 AS |
429 | goto out; |
430 | err: | |
431 | rc = 1; | |
432 | ||
433 | out: | |
434 | for (i = 0; i < 6; i++) | |
435 | if (allow_prog[i] > 0) | |
436 | close(allow_prog[i]); | |
437 | close(cg1); | |
438 | close(cg2); | |
439 | close(cg3); | |
440 | close(cg4); | |
441 | close(cg5); | |
442 | cleanup_cgroup_environment(); | |
443 | if (!rc) | |
444 | printf("### multi:PASS\n"); | |
7f677633 | 445 | else |
39323e78 | 446 | printf("### multi:FAIL\n"); |
9b474ece SD |
447 | return rc; |
448 | } | |
39323e78 AS |
449 | |
450 | int main(int argc, char **argv) | |
451 | { | |
452 | int rc = 0; | |
453 | ||
454 | rc = test_foo_bar(); | |
455 | if (rc) | |
456 | return rc; | |
457 | ||
458 | return test_multiprog(); | |
459 | } |