1 // SPDX-License-Identifier: GPL-2.0
3 /* eBPF example program:
5 * - Creates arraymap in kernel with 4 bytes keys and 8 byte values
9 * The eBPF program accesses the map passed in to store two pieces of
10 * information. The number of invocations of the program, which maps
11 * to the number of packets received, is stored to key 0. Key 1 is
12 * incremented on each iteration by the number of bytes stored in
13 * the skb. The program also stores the number of received bytes
14 * in the cgroup storage.
16 * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
18 * - Every second, reads map[0] and map[1] to see how many bytes and
19 * packets were seen on any socket of tasks in the given cgroup.
27 #include <sys/resource.h>
30 #include <linux/filter.h>
32 #include <linux/bpf.h>
36 #include "bpf_rlimit.h"
37 #include "cgroup_helpers.h"
40 #define BAR "/foo/bar/"
41 #define PING_CMD "ping -q -c1 -w1 127.0.0.1 > /dev/null"
43 char bpf_log_buf
[BPF_LOG_BUF_SIZE
];
46 #define debug(args...) printf(args)
48 #define debug(args...)
51 static int prog_load(int verdict
)
54 struct bpf_insn prog
[] = {
55 BPF_MOV64_IMM(BPF_REG_0
, verdict
), /* r0 = verdict */
58 size_t insns_cnt
= sizeof(prog
) / sizeof(struct bpf_insn
);
60 ret
= bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB
,
61 prog
, insns_cnt
, "GPL", 0,
62 bpf_log_buf
, BPF_LOG_BUF_SIZE
);
65 log_err("Loading program");
66 printf("Output from verifier:\n%s\n-------\n", bpf_log_buf
);
72 static int test_foo_bar(void)
74 int drop_prog
, allow_prog
, foo
= 0, bar
= 0, rc
= 0;
76 allow_prog
= prog_load(1);
80 drop_prog
= prog_load(0);
84 if (setup_cgroup_environment())
87 /* Create cgroup /foo, get fd, and join it */
88 foo
= create_and_get_cgroup(FOO
);
95 if (bpf_prog_attach(drop_prog
, foo
, BPF_CGROUP_INET_EGRESS
,
96 BPF_F_ALLOW_OVERRIDE
)) {
97 log_err("Attaching prog to /foo");
101 debug("Attached DROP prog. This ping in cgroup /foo should fail...\n");
102 assert(system(PING_CMD
) != 0);
104 /* Create cgroup /foo/bar, get fd, and join it */
105 bar
= create_and_get_cgroup(BAR
);
109 if (join_cgroup(BAR
))
112 debug("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
113 assert(system(PING_CMD
) != 0);
115 if (bpf_prog_attach(allow_prog
, bar
, BPF_CGROUP_INET_EGRESS
,
116 BPF_F_ALLOW_OVERRIDE
)) {
117 log_err("Attaching prog to /foo/bar");
121 debug("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
122 assert(system(PING_CMD
) == 0);
124 if (bpf_prog_detach(bar
, BPF_CGROUP_INET_EGRESS
)) {
125 log_err("Detaching program from /foo/bar");
129 debug("Detached PASS from /foo/bar while DROP is attached to /foo.\n"
130 "This ping in cgroup /foo/bar should fail...\n");
131 assert(system(PING_CMD
) != 0);
133 if (bpf_prog_attach(allow_prog
, bar
, BPF_CGROUP_INET_EGRESS
,
134 BPF_F_ALLOW_OVERRIDE
)) {
135 log_err("Attaching prog to /foo/bar");
139 if (bpf_prog_detach(foo
, BPF_CGROUP_INET_EGRESS
)) {
140 log_err("Detaching program from /foo");
144 debug("Attached PASS from /foo/bar and detached DROP from /foo.\n"
145 "This ping in cgroup /foo/bar should pass...\n");
146 assert(system(PING_CMD
) == 0);
148 if (bpf_prog_attach(allow_prog
, bar
, BPF_CGROUP_INET_EGRESS
,
149 BPF_F_ALLOW_OVERRIDE
)) {
150 log_err("Attaching prog to /foo/bar");
154 if (!bpf_prog_attach(allow_prog
, bar
, BPF_CGROUP_INET_EGRESS
, 0)) {
156 log_err("Unexpected success attaching prog to /foo/bar");
160 if (bpf_prog_detach(bar
, BPF_CGROUP_INET_EGRESS
)) {
161 log_err("Detaching program from /foo/bar");
165 if (!bpf_prog_detach(foo
, BPF_CGROUP_INET_EGRESS
)) {
167 log_err("Unexpected success in double detach from /foo");
171 if (bpf_prog_attach(allow_prog
, foo
, BPF_CGROUP_INET_EGRESS
, 0)) {
172 log_err("Attaching non-overridable prog to /foo");
176 if (!bpf_prog_attach(allow_prog
, bar
, BPF_CGROUP_INET_EGRESS
, 0)) {
178 log_err("Unexpected success attaching non-overridable prog to /foo/bar");
182 if (!bpf_prog_attach(allow_prog
, bar
, BPF_CGROUP_INET_EGRESS
,
183 BPF_F_ALLOW_OVERRIDE
)) {
185 log_err("Unexpected success attaching overridable prog to /foo/bar");
189 if (!bpf_prog_attach(allow_prog
, foo
, BPF_CGROUP_INET_EGRESS
,
190 BPF_F_ALLOW_OVERRIDE
)) {
192 log_err("Unexpected success attaching overridable prog to /foo");
196 if (bpf_prog_attach(drop_prog
, foo
, BPF_CGROUP_INET_EGRESS
, 0)) {
197 log_err("Attaching different non-overridable prog to /foo");
209 cleanup_cgroup_environment();
211 printf("#override:PASS\n");
213 printf("#override:FAIL\n");
217 static int map_fd
= -1;
219 static int prog_load_cnt(int verdict
, int val
)
221 int cgroup_storage_fd
, percpu_cgroup_storage_fd
;
224 map_fd
= bpf_create_map(BPF_MAP_TYPE_ARRAY
, 4, 8, 1, 0);
226 printf("failed to create map '%s'\n", strerror(errno
));
230 cgroup_storage_fd
= bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE
,
231 sizeof(struct bpf_cgroup_storage_key
), 8, 0, 0);
232 if (cgroup_storage_fd
< 0) {
233 printf("failed to create map '%s'\n", strerror(errno
));
237 percpu_cgroup_storage_fd
= bpf_create_map(
238 BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE
,
239 sizeof(struct bpf_cgroup_storage_key
), 8, 0, 0);
240 if (percpu_cgroup_storage_fd
< 0) {
241 printf("failed to create map '%s'\n", strerror(errno
));
245 struct bpf_insn prog
[] = {
246 BPF_MOV32_IMM(BPF_REG_0
, 0),
247 BPF_STX_MEM(BPF_W
, BPF_REG_10
, BPF_REG_0
, -4), /* *(u32 *)(fp - 4) = r0 */
248 BPF_MOV64_REG(BPF_REG_2
, BPF_REG_10
),
249 BPF_ALU64_IMM(BPF_ADD
, BPF_REG_2
, -4), /* r2 = fp - 4 */
250 BPF_LD_MAP_FD(BPF_REG_1
, map_fd
),
251 BPF_RAW_INSN(BPF_JMP
| BPF_CALL
, 0, 0, 0, BPF_FUNC_map_lookup_elem
),
252 BPF_JMP_IMM(BPF_JEQ
, BPF_REG_0
, 0, 2),
253 BPF_MOV64_IMM(BPF_REG_1
, val
), /* r1 = 1 */
254 BPF_RAW_INSN(BPF_STX
| BPF_XADD
| BPF_DW
, BPF_REG_0
, BPF_REG_1
, 0, 0), /* xadd r0 += r1 */
256 BPF_LD_MAP_FD(BPF_REG_1
, cgroup_storage_fd
),
257 BPF_MOV64_IMM(BPF_REG_2
, 0),
258 BPF_RAW_INSN(BPF_JMP
| BPF_CALL
, 0, 0, 0, BPF_FUNC_get_local_storage
),
259 BPF_MOV64_IMM(BPF_REG_1
, val
),
260 BPF_RAW_INSN(BPF_STX
| BPF_XADD
| BPF_W
, BPF_REG_0
, BPF_REG_1
, 0, 0),
262 BPF_LD_MAP_FD(BPF_REG_1
, percpu_cgroup_storage_fd
),
263 BPF_MOV64_IMM(BPF_REG_2
, 0),
264 BPF_RAW_INSN(BPF_JMP
| BPF_CALL
, 0, 0, 0, BPF_FUNC_get_local_storage
),
265 BPF_LDX_MEM(BPF_W
, BPF_REG_3
, BPF_REG_0
, 0),
266 BPF_ALU64_IMM(BPF_ADD
, BPF_REG_3
, 0x1),
267 BPF_STX_MEM(BPF_W
, BPF_REG_0
, BPF_REG_3
, 0),
269 BPF_MOV64_IMM(BPF_REG_0
, verdict
), /* r0 = verdict */
272 size_t insns_cnt
= sizeof(prog
) / sizeof(struct bpf_insn
);
275 ret
= bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB
,
276 prog
, insns_cnt
, "GPL", 0,
277 bpf_log_buf
, BPF_LOG_BUF_SIZE
);
280 log_err("Loading program");
281 printf("Output from verifier:\n%s\n-------\n", bpf_log_buf
);
284 close(cgroup_storage_fd
);
289 static int test_multiprog(void)
291 __u32 prog_ids
[4], prog_cnt
= 0, attach_flags
, saved_prog_id
;
292 int cg1
= 0, cg2
= 0, cg3
= 0, cg4
= 0, cg5
= 0, key
= 0;
293 int drop_prog
, allow_prog
[6] = {}, rc
= 0;
294 unsigned long long value
;
297 for (i
= 0; i
< 6; i
++) {
298 allow_prog
[i
] = prog_load_cnt(1, 1 << i
);
302 drop_prog
= prog_load_cnt(0, 1);
306 if (setup_cgroup_environment())
309 cg1
= create_and_get_cgroup("/cg1");
312 cg2
= create_and_get_cgroup("/cg1/cg2");
315 cg3
= create_and_get_cgroup("/cg1/cg2/cg3");
318 cg4
= create_and_get_cgroup("/cg1/cg2/cg3/cg4");
321 cg5
= create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5");
325 if (join_cgroup("/cg1/cg2/cg3/cg4/cg5"))
328 if (bpf_prog_attach(allow_prog
[0], cg1
, BPF_CGROUP_INET_EGRESS
,
329 BPF_F_ALLOW_MULTI
)) {
330 log_err("Attaching prog to cg1");
333 if (!bpf_prog_attach(allow_prog
[0], cg1
, BPF_CGROUP_INET_EGRESS
,
334 BPF_F_ALLOW_MULTI
)) {
335 log_err("Unexpected success attaching the same prog to cg1");
338 if (bpf_prog_attach(allow_prog
[1], cg1
, BPF_CGROUP_INET_EGRESS
,
339 BPF_F_ALLOW_MULTI
)) {
340 log_err("Attaching prog2 to cg1");
343 if (bpf_prog_attach(allow_prog
[2], cg2
, BPF_CGROUP_INET_EGRESS
,
344 BPF_F_ALLOW_OVERRIDE
)) {
345 log_err("Attaching prog to cg2");
348 if (bpf_prog_attach(allow_prog
[3], cg3
, BPF_CGROUP_INET_EGRESS
,
349 BPF_F_ALLOW_MULTI
)) {
350 log_err("Attaching prog to cg3");
353 if (bpf_prog_attach(allow_prog
[4], cg4
, BPF_CGROUP_INET_EGRESS
,
354 BPF_F_ALLOW_OVERRIDE
)) {
355 log_err("Attaching prog to cg4");
358 if (bpf_prog_attach(allow_prog
[5], cg5
, BPF_CGROUP_INET_EGRESS
, 0)) {
359 log_err("Attaching prog to cg5");
362 assert(system(PING_CMD
) == 0);
363 assert(bpf_map_lookup_elem(map_fd
, &key
, &value
) == 0);
364 assert(value
== 1 + 2 + 8 + 32);
366 /* query the number of effective progs in cg5 */
367 assert(bpf_prog_query(cg5
, BPF_CGROUP_INET_EGRESS
, BPF_F_QUERY_EFFECTIVE
,
368 NULL
, NULL
, &prog_cnt
) == 0);
369 assert(prog_cnt
== 4);
370 /* retrieve prog_ids of effective progs in cg5 */
371 assert(bpf_prog_query(cg5
, BPF_CGROUP_INET_EGRESS
, BPF_F_QUERY_EFFECTIVE
,
372 &attach_flags
, prog_ids
, &prog_cnt
) == 0);
373 assert(prog_cnt
== 4);
374 assert(attach_flags
== 0);
375 saved_prog_id
= prog_ids
[0];
376 /* check enospc handling */
379 assert(bpf_prog_query(cg5
, BPF_CGROUP_INET_EGRESS
, BPF_F_QUERY_EFFECTIVE
,
380 &attach_flags
, prog_ids
, &prog_cnt
) == -1 &&
382 assert(prog_cnt
== 4);
383 /* check that prog_ids are returned even when buffer is too small */
384 assert(prog_ids
[0] == saved_prog_id
);
385 /* retrieve prog_id of single attached prog in cg5 */
387 assert(bpf_prog_query(cg5
, BPF_CGROUP_INET_EGRESS
, 0,
388 NULL
, prog_ids
, &prog_cnt
) == 0);
389 assert(prog_cnt
== 1);
390 assert(prog_ids
[0] == saved_prog_id
);
392 /* detach bottom program and ping again */
393 if (bpf_prog_detach2(-1, cg5
, BPF_CGROUP_INET_EGRESS
)) {
394 log_err("Detaching prog from cg5");
398 assert(bpf_map_update_elem(map_fd
, &key
, &value
, 0) == 0);
399 assert(system(PING_CMD
) == 0);
400 assert(bpf_map_lookup_elem(map_fd
, &key
, &value
) == 0);
401 assert(value
== 1 + 2 + 8 + 16);
403 /* detach 3rd from bottom program and ping again */
405 if (!bpf_prog_detach2(0, cg3
, BPF_CGROUP_INET_EGRESS
)) {
406 log_err("Unexpected success on detach from cg3");
409 if (bpf_prog_detach2(allow_prog
[3], cg3
, BPF_CGROUP_INET_EGRESS
)) {
410 log_err("Detaching from cg3");
414 assert(bpf_map_update_elem(map_fd
, &key
, &value
, 0) == 0);
415 assert(system(PING_CMD
) == 0);
416 assert(bpf_map_lookup_elem(map_fd
, &key
, &value
) == 0);
417 assert(value
== 1 + 2 + 16);
419 /* detach 2nd from bottom program and ping again */
420 if (bpf_prog_detach2(-1, cg4
, BPF_CGROUP_INET_EGRESS
)) {
421 log_err("Detaching prog from cg4");
425 assert(bpf_map_update_elem(map_fd
, &key
, &value
, 0) == 0);
426 assert(system(PING_CMD
) == 0);
427 assert(bpf_map_lookup_elem(map_fd
, &key
, &value
) == 0);
428 assert(value
== 1 + 2 + 4);
431 assert(bpf_prog_query(cg5
, BPF_CGROUP_INET_EGRESS
, BPF_F_QUERY_EFFECTIVE
,
432 &attach_flags
, prog_ids
, &prog_cnt
) == 0);
433 assert(prog_cnt
== 3);
434 assert(attach_flags
== 0);
435 assert(bpf_prog_query(cg5
, BPF_CGROUP_INET_EGRESS
, 0,
436 NULL
, prog_ids
, &prog_cnt
) == 0);
437 assert(prog_cnt
== 0);
443 for (i
= 0; i
< 6; i
++)
444 if (allow_prog
[i
] > 0)
445 close(allow_prog
[i
]);
451 cleanup_cgroup_environment();
453 printf("#multi:PASS\n");
455 printf("#multi:FAIL\n");
459 static int test_autodetach(void)
461 __u32 prog_cnt
= 4, attach_flags
;
462 int allow_prog
[2] = {0};
463 __u32 prog_ids
[2] = {0};
464 int cg
= 0, i
, rc
= -1;
468 for (i
= 0; i
< ARRAY_SIZE(allow_prog
); i
++) {
469 allow_prog
[i
] = prog_load_cnt(1, 1 << i
);
474 if (setup_cgroup_environment())
477 /* create a cgroup, attach two programs and remember their ids */
478 cg
= create_and_get_cgroup("/cg_autodetach");
482 if (join_cgroup("/cg_autodetach"))
485 for (i
= 0; i
< ARRAY_SIZE(allow_prog
); i
++) {
486 if (bpf_prog_attach(allow_prog
[i
], cg
, BPF_CGROUP_INET_EGRESS
,
487 BPF_F_ALLOW_MULTI
)) {
488 log_err("Attaching prog[%d] to cg:egress", i
);
493 /* make sure that programs are attached and run some traffic */
494 assert(bpf_prog_query(cg
, BPF_CGROUP_INET_EGRESS
, 0, &attach_flags
,
495 prog_ids
, &prog_cnt
) == 0);
496 assert(system(PING_CMD
) == 0);
498 /* allocate some memory (4Mb) to pin the original cgroup */
499 ptr
= malloc(4 * (1 << 20));
503 /* close programs and cgroup fd */
504 for (i
= 0; i
< ARRAY_SIZE(allow_prog
); i
++) {
505 close(allow_prog
[i
]);
512 /* leave the cgroup and remove it. don't detach programs */
513 cleanup_cgroup_environment();
515 /* wait for the asynchronous auto-detachment.
516 * wait for no more than 5 sec and give up.
518 for (i
= 0; i
< ARRAY_SIZE(prog_ids
); i
++) {
519 for (attempts
= 5; attempts
>= 0; attempts
--) {
520 int fd
= bpf_prog_get_fd_by_id(prog_ids
[i
]);
525 /* don't leave the fd open */
537 for (i
= 0; i
< ARRAY_SIZE(allow_prog
); i
++)
538 if (allow_prog
[i
] > 0)
539 close(allow_prog
[i
]);
543 cleanup_cgroup_environment();
545 printf("#autodetach:PASS\n");
547 printf("#autodetach:FAIL\n");
553 int (*tests
[])(void) = {
561 for (i
= 0; i
< ARRAY_SIZE(tests
); i
++)
566 printf("test_cgroup_attach:FAIL\n");
568 printf("test_cgroup_attach:PASS\n");
570 return errors
? EXIT_FAILURE
: EXIT_SUCCESS
;