• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 
3 /* eBPF example program:
4  *
5  * - Creates arraymap in kernel with 4 bytes keys and 8 byte values
6  *
7  * - Loads eBPF program
8  *
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.
15  *
16  * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
17  *
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.
20  */
21 
22 #define _GNU_SOURCE
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <assert.h>
27 #include <sys/resource.h>
28 #include <sys/time.h>
29 #include <unistd.h>
30 #include <linux/filter.h>
31 
32 #include <linux/bpf.h>
33 #include <bpf/bpf.h>
34 
35 #include "bpf_util.h"
36 #include "bpf_rlimit.h"
37 #include "cgroup_helpers.h"
38 
39 #define FOO		"/foo"
40 #define BAR		"/foo/bar/"
41 #define PING_CMD	"ping -q -c1 -w1 127.0.0.1 > /dev/null"
42 
43 char bpf_log_buf[BPF_LOG_BUF_SIZE];
44 
45 #ifdef DEBUG
46 #define debug(args...) printf(args)
47 #else
48 #define debug(args...)
49 #endif
50 
prog_load(int verdict)51 static int prog_load(int verdict)
52 {
53 	int ret;
54 	struct bpf_insn prog[] = {
55 		BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
56 		BPF_EXIT_INSN(),
57 	};
58 	size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
59 
60 	ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
61 			       prog, insns_cnt, "GPL", 0,
62 			       bpf_log_buf, BPF_LOG_BUF_SIZE);
63 
64 	if (ret < 0) {
65 		log_err("Loading program");
66 		printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
67 		return 0;
68 	}
69 	return ret;
70 }
71 
test_foo_bar(void)72 static int test_foo_bar(void)
73 {
74 	int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0;
75 
76 	allow_prog = prog_load(1);
77 	if (!allow_prog)
78 		goto err;
79 
80 	drop_prog = prog_load(0);
81 	if (!drop_prog)
82 		goto err;
83 
84 	if (setup_cgroup_environment())
85 		goto err;
86 
87 	/* Create cgroup /foo, get fd, and join it */
88 	foo = create_and_get_cgroup(FOO);
89 	if (foo < 0)
90 		goto err;
91 
92 	if (join_cgroup(FOO))
93 		goto err;
94 
95 	if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS,
96 			    BPF_F_ALLOW_OVERRIDE)) {
97 		log_err("Attaching prog to /foo");
98 		goto err;
99 	}
100 
101 	debug("Attached DROP prog. This ping in cgroup /foo should fail...\n");
102 	assert(system(PING_CMD) != 0);
103 
104 	/* Create cgroup /foo/bar, get fd, and join it */
105 	bar = create_and_get_cgroup(BAR);
106 	if (bar < 0)
107 		goto err;
108 
109 	if (join_cgroup(BAR))
110 		goto err;
111 
112 	debug("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
113 	assert(system(PING_CMD) != 0);
114 
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");
118 		goto err;
119 	}
120 
121 	debug("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
122 	assert(system(PING_CMD) == 0);
123 
124 	if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
125 		log_err("Detaching program from /foo/bar");
126 		goto err;
127 	}
128 
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);
132 
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");
136 		goto err;
137 	}
138 
139 	if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
140 		log_err("Detaching program from /foo");
141 		goto err;
142 	}
143 
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);
147 
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");
151 		goto err;
152 	}
153 
154 	if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
155 		errno = 0;
156 		log_err("Unexpected success attaching prog to /foo/bar");
157 		goto err;
158 	}
159 
160 	if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
161 		log_err("Detaching program from /foo/bar");
162 		goto err;
163 	}
164 
165 	if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
166 		errno = 0;
167 		log_err("Unexpected success in double detach from /foo");
168 		goto err;
169 	}
170 
171 	if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
172 		log_err("Attaching non-overridable prog to /foo");
173 		goto err;
174 	}
175 
176 	if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
177 		errno = 0;
178 		log_err("Unexpected success attaching non-overridable prog to /foo/bar");
179 		goto err;
180 	}
181 
182 	if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
183 			     BPF_F_ALLOW_OVERRIDE)) {
184 		errno = 0;
185 		log_err("Unexpected success attaching overridable prog to /foo/bar");
186 		goto err;
187 	}
188 
189 	if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS,
190 			     BPF_F_ALLOW_OVERRIDE)) {
191 		errno = 0;
192 		log_err("Unexpected success attaching overridable prog to /foo");
193 		goto err;
194 	}
195 
196 	if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
197 		log_err("Attaching different non-overridable prog to /foo");
198 		goto err;
199 	}
200 
201 	goto out;
202 
203 err:
204 	rc = 1;
205 
206 out:
207 	close(foo);
208 	close(bar);
209 	cleanup_cgroup_environment();
210 	if (!rc)
211 		printf("#override:PASS\n");
212 	else
213 		printf("#override:FAIL\n");
214 	return rc;
215 }
216 
217 static int map_fd = -1;
218 
prog_load_cnt(int verdict,int val)219 static int prog_load_cnt(int verdict, int val)
220 {
221 	int cgroup_storage_fd, percpu_cgroup_storage_fd;
222 
223 	if (map_fd < 0)
224 		map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0);
225 	if (map_fd < 0) {
226 		printf("failed to create map '%s'\n", strerror(errno));
227 		return -1;
228 	}
229 
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));
234 		return -1;
235 	}
236 
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));
242 		return -1;
243 	}
244 
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 */
255 
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),
261 
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),
268 
269 		BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
270 		BPF_EXIT_INSN(),
271 	};
272 	size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
273 	int ret;
274 
275 	ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
276 			       prog, insns_cnt, "GPL", 0,
277 			       bpf_log_buf, BPF_LOG_BUF_SIZE);
278 
279 	if (ret < 0) {
280 		log_err("Loading program");
281 		printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
282 		return 0;
283 	}
284 	close(cgroup_storage_fd);
285 	return ret;
286 }
287 
288 
test_multiprog(void)289 static int test_multiprog(void)
290 {
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;
295 	int i = 0;
296 
297 	for (i = 0; i < 6; i++) {
298 		allow_prog[i] = prog_load_cnt(1, 1 << i);
299 		if (!allow_prog[i])
300 			goto err;
301 	}
302 	drop_prog = prog_load_cnt(0, 1);
303 	if (!drop_prog)
304 		goto err;
305 
306 	if (setup_cgroup_environment())
307 		goto err;
308 
309 	cg1 = create_and_get_cgroup("/cg1");
310 	if (cg1 < 0)
311 		goto err;
312 	cg2 = create_and_get_cgroup("/cg1/cg2");
313 	if (cg2 < 0)
314 		goto err;
315 	cg3 = create_and_get_cgroup("/cg1/cg2/cg3");
316 	if (cg3 < 0)
317 		goto err;
318 	cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4");
319 	if (cg4 < 0)
320 		goto err;
321 	cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5");
322 	if (cg5 < 0)
323 		goto err;
324 
325 	if (join_cgroup("/cg1/cg2/cg3/cg4/cg5"))
326 		goto err;
327 
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");
331 		goto err;
332 	}
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");
336 		goto err;
337 	}
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");
341 		goto err;
342 	}
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");
346 		goto err;
347 	}
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");
351 		goto err;
352 	}
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");
356 		goto err;
357 	}
358 	if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) {
359 		log_err("Attaching prog to cg5");
360 		goto err;
361 	}
362 	assert(system(PING_CMD) == 0);
363 	assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
364 	assert(value == 1 + 2 + 8 + 32);
365 
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 */
377 	prog_ids[0] = 0;
378 	prog_cnt = 2;
379 	assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
380 			      &attach_flags, prog_ids, &prog_cnt) == -1 &&
381 	       errno == ENOSPC);
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 */
386 	prog_ids[0] = 0;
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);
391 
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");
395 		goto err;
396 	}
397 	value = 0;
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);
402 
403 	/* detach 3rd from bottom program and ping again */
404 	errno = 0;
405 	if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) {
406 		log_err("Unexpected success on detach from cg3");
407 		goto err;
408 	}
409 	if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) {
410 		log_err("Detaching from cg3");
411 		goto err;
412 	}
413 	value = 0;
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);
418 
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");
422 		goto err;
423 	}
424 	value = 0;
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);
429 
430 	prog_cnt = 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);
438 	goto out;
439 err:
440 	rc = 1;
441 
442 out:
443 	for (i = 0; i < 6; i++)
444 		if (allow_prog[i] > 0)
445 			close(allow_prog[i]);
446 	close(cg1);
447 	close(cg2);
448 	close(cg3);
449 	close(cg4);
450 	close(cg5);
451 	cleanup_cgroup_environment();
452 	if (!rc)
453 		printf("#multi:PASS\n");
454 	else
455 		printf("#multi:FAIL\n");
456 	return rc;
457 }
458 
test_autodetach(void)459 static int test_autodetach(void)
460 {
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;
465 	void *ptr = NULL;
466 	int attempts;
467 
468 	for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
469 		allow_prog[i] = prog_load_cnt(1, 1 << i);
470 		if (!allow_prog[i])
471 			goto err;
472 	}
473 
474 	if (setup_cgroup_environment())
475 		goto err;
476 
477 	/* create a cgroup, attach two programs and remember their ids */
478 	cg = create_and_get_cgroup("/cg_autodetach");
479 	if (cg < 0)
480 		goto err;
481 
482 	if (join_cgroup("/cg_autodetach"))
483 		goto err;
484 
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);
489 			goto err;
490 		}
491 	}
492 
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);
497 
498 	/* allocate some memory (4Mb) to pin the original cgroup */
499 	ptr = malloc(4 * (1 << 20));
500 	if (!ptr)
501 		goto err;
502 
503 	/* close programs and cgroup fd */
504 	for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
505 		close(allow_prog[i]);
506 		allow_prog[i] = 0;
507 	}
508 
509 	close(cg);
510 	cg = 0;
511 
512 	/* leave the cgroup and remove it. don't detach programs */
513 	cleanup_cgroup_environment();
514 
515 	/* wait for the asynchronous auto-detachment.
516 	 * wait for no more than 5 sec and give up.
517 	 */
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]);
521 
522 			if (fd < 0)
523 				break;
524 
525 			/* don't leave the fd open */
526 			close(fd);
527 
528 			if (!attempts)
529 				goto err;
530 
531 			sleep(1);
532 		}
533 	}
534 
535 	rc = 0;
536 err:
537 	for (i = 0; i < ARRAY_SIZE(allow_prog); i++)
538 		if (allow_prog[i] > 0)
539 			close(allow_prog[i]);
540 	if (cg)
541 		close(cg);
542 	free(ptr);
543 	cleanup_cgroup_environment();
544 	if (!rc)
545 		printf("#autodetach:PASS\n");
546 	else
547 		printf("#autodetach:FAIL\n");
548 	return rc;
549 }
550 
main(void)551 int main(void)
552 {
553 	int (*tests[])(void) = {
554 		test_foo_bar,
555 		test_multiprog,
556 		test_autodetach,
557 	};
558 	int errors = 0;
559 	int i;
560 
561 	for (i = 0; i < ARRAY_SIZE(tests); i++)
562 		if (tests[i]())
563 			errors++;
564 
565 	if (errors)
566 		printf("test_cgroup_attach:FAIL\n");
567 	else
568 		printf("test_cgroup_attach:PASS\n");
569 
570 	return errors ? EXIT_FAILURE : EXIT_SUCCESS;
571 }
572