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
11  *   the skb. The program also stores the number of received bytes
12  *   in the cgroup storage.
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>
25 #include <sys/resource.h>
26 #include <sys/time.h>
27 #include <unistd.h>
28 
29 #include <linux/bpf.h>
30 #include <bpf/bpf.h>
31 
32 #include "bpf_insn.h"
33 #include "bpf_rlimit.h"
34 #include "cgroup_helpers.h"
35 
36 #define FOO		"/foo"
37 #define BAR		"/foo/bar/"
38 #define PING_CMD	"ping -c1 -w1 127.0.0.1 > /dev/null"
39 
40 char bpf_log_buf[BPF_LOG_BUF_SIZE];
41 
prog_load(int verdict)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 	};
49 	size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
50 
51 	ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
52 			       prog, insns_cnt, "GPL", 0,
53 			       bpf_log_buf, BPF_LOG_BUF_SIZE);
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 
test_foo_bar(void)63 static int test_foo_bar(void)
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 
86 	if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS,
87 			    BPF_F_ALLOW_OVERRIDE)) {
88 		log_err("Attaching prog to /foo");
89 		goto err;
90 	}
91 
92 	printf("Attached DROP prog. This ping in cgroup /foo should fail...\n");
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 
103 	printf("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
104 	assert(system(PING_CMD) != 0);
105 
106 	if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
107 			    BPF_F_ALLOW_OVERRIDE)) {
108 		log_err("Attaching prog to /foo/bar");
109 		goto err;
110 	}
111 
112 	printf("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
113 	assert(system(PING_CMD) == 0);
114 
115 	if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
116 		log_err("Detaching program from /foo/bar");
117 		goto err;
118 	}
119 
120 	printf("Detached PASS from /foo/bar while DROP is attached to /foo.\n"
121 	       "This ping in cgroup /foo/bar should fail...\n");
122 	assert(system(PING_CMD) != 0);
123 
124 	if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
125 			    BPF_F_ALLOW_OVERRIDE)) {
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 
135 	printf("Attached PASS from /foo/bar and detached DROP from /foo.\n"
136 	       "This ping in cgroup /foo/bar should pass...\n");
137 	assert(system(PING_CMD) == 0);
138 
139 	if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
140 			    BPF_F_ALLOW_OVERRIDE)) {
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 
173 	if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
174 			     BPF_F_ALLOW_OVERRIDE)) {
175 		errno = 0;
176 		log_err("Unexpected success attaching overridable prog to /foo/bar");
177 		goto err;
178 	}
179 
180 	if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS,
181 			     BPF_F_ALLOW_OVERRIDE)) {
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 
192 	goto out;
193 
194 err:
195 	rc = 1;
196 
197 out:
198 	close(foo);
199 	close(bar);
200 	cleanup_cgroup_environment();
201 	if (!rc)
202 		printf("### override:PASS\n");
203 	else
204 		printf("### override:FAIL\n");
205 	return rc;
206 }
207 
208 static int map_fd = -1;
209 
prog_load_cnt(int verdict,int val)210 static int prog_load_cnt(int verdict, int val)
211 {
212 	int cgroup_storage_fd;
213 
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 
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 
228 	struct bpf_insn prog[] = {
229 		BPF_MOV32_IMM(BPF_REG_0, 0),
230 		BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
231 		BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
232 		BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
233 		BPF_LD_MAP_FD(BPF_REG_1, map_fd),
234 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
235 		BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
236 		BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */
237 		BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
238 		BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd),
239 		BPF_MOV64_IMM(BPF_REG_2, 0),
240 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
241 		BPF_MOV64_IMM(BPF_REG_1, val),
242 		BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0),
243 		BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
244 		BPF_EXIT_INSN(),
245 	};
246 	size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
247 	int ret;
248 
249 	ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
250 			       prog, insns_cnt, "GPL", 0,
251 			       bpf_log_buf, BPF_LOG_BUF_SIZE);
252 
253 	if (ret < 0) {
254 		log_err("Loading program");
255 		printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
256 		return 0;
257 	}
258 	close(cgroup_storage_fd);
259 	return ret;
260 }
261 
262 
test_multiprog(void)263 static int test_multiprog(void)
264 {
265 	__u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id;
266 	int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0;
267 	int drop_prog, allow_prog[6] = {}, rc = 0;
268 	unsigned long long value;
269 	int i = 0;
270 
271 	for (i = 0; i < 6; i++) {
272 		allow_prog[i] = prog_load_cnt(1, 1 << i);
273 		if (!allow_prog[i])
274 			goto err;
275 	}
276 	drop_prog = prog_load_cnt(0, 1);
277 	if (!drop_prog)
278 		goto err;
279 
280 	if (setup_cgroup_environment())
281 		goto err;
282 
283 	cg1 = create_and_get_cgroup("/cg1");
284 	if (!cg1)
285 		goto err;
286 	cg2 = create_and_get_cgroup("/cg1/cg2");
287 	if (!cg2)
288 		goto err;
289 	cg3 = create_and_get_cgroup("/cg1/cg2/cg3");
290 	if (!cg3)
291 		goto err;
292 	cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4");
293 	if (!cg4)
294 		goto err;
295 	cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5");
296 	if (!cg5)
297 		goto err;
298 
299 	if (join_cgroup("/cg1/cg2/cg3/cg4/cg5"))
300 		goto err;
301 
302 	if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
303 			    BPF_F_ALLOW_MULTI)) {
304 		log_err("Attaching prog to cg1");
305 		goto err;
306 	}
307 	if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
308 			     BPF_F_ALLOW_MULTI)) {
309 		log_err("Unexpected success attaching the same prog to cg1");
310 		goto err;
311 	}
312 	if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS,
313 			    BPF_F_ALLOW_MULTI)) {
314 		log_err("Attaching prog2 to cg1");
315 		goto err;
316 	}
317 	if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS,
318 			    BPF_F_ALLOW_OVERRIDE)) {
319 		log_err("Attaching prog to cg2");
320 		goto err;
321 	}
322 	if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS,
323 			    BPF_F_ALLOW_MULTI)) {
324 		log_err("Attaching prog to cg3");
325 		goto err;
326 	}
327 	if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS,
328 			    BPF_F_ALLOW_OVERRIDE)) {
329 		log_err("Attaching prog to cg4");
330 		goto err;
331 	}
332 	if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) {
333 		log_err("Attaching prog to cg5");
334 		goto err;
335 	}
336 	assert(system(PING_CMD) == 0);
337 	assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
338 	assert(value == 1 + 2 + 8 + 32);
339 
340 	/* query the number of effective progs in cg5 */
341 	assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
342 			      NULL, NULL, &prog_cnt) == 0);
343 	assert(prog_cnt == 4);
344 	/* retrieve prog_ids of effective progs in cg5 */
345 	assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
346 			      &attach_flags, prog_ids, &prog_cnt) == 0);
347 	assert(prog_cnt == 4);
348 	assert(attach_flags == 0);
349 	saved_prog_id = prog_ids[0];
350 	/* check enospc handling */
351 	prog_ids[0] = 0;
352 	prog_cnt = 2;
353 	assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
354 			      &attach_flags, prog_ids, &prog_cnt) == -1 &&
355 	       errno == ENOSPC);
356 	assert(prog_cnt == 4);
357 	/* check that prog_ids are returned even when buffer is too small */
358 	assert(prog_ids[0] == saved_prog_id);
359 	/* retrieve prog_id of single attached prog in cg5 */
360 	prog_ids[0] = 0;
361 	assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
362 			      NULL, prog_ids, &prog_cnt) == 0);
363 	assert(prog_cnt == 1);
364 	assert(prog_ids[0] == saved_prog_id);
365 
366 	/* detach bottom program and ping again */
367 	if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) {
368 		log_err("Detaching prog from cg5");
369 		goto err;
370 	}
371 	value = 0;
372 	assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
373 	assert(system(PING_CMD) == 0);
374 	assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
375 	assert(value == 1 + 2 + 8 + 16);
376 
377 	/* detach 3rd from bottom program and ping again */
378 	errno = 0;
379 	if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) {
380 		log_err("Unexpected success on detach from cg3");
381 		goto err;
382 	}
383 	if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) {
384 		log_err("Detaching from cg3");
385 		goto err;
386 	}
387 	value = 0;
388 	assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
389 	assert(system(PING_CMD) == 0);
390 	assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
391 	assert(value == 1 + 2 + 16);
392 
393 	/* detach 2nd from bottom program and ping again */
394 	if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) {
395 		log_err("Detaching prog from cg4");
396 		goto err;
397 	}
398 	value = 0;
399 	assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
400 	assert(system(PING_CMD) == 0);
401 	assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
402 	assert(value == 1 + 2 + 4);
403 
404 	prog_cnt = 4;
405 	assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
406 			      &attach_flags, prog_ids, &prog_cnt) == 0);
407 	assert(prog_cnt == 3);
408 	assert(attach_flags == 0);
409 	assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
410 			      NULL, prog_ids, &prog_cnt) == 0);
411 	assert(prog_cnt == 0);
412 	goto out;
413 err:
414 	rc = 1;
415 
416 out:
417 	for (i = 0; i < 6; i++)
418 		if (allow_prog[i] > 0)
419 			close(allow_prog[i]);
420 	close(cg1);
421 	close(cg2);
422 	close(cg3);
423 	close(cg4);
424 	close(cg5);
425 	cleanup_cgroup_environment();
426 	if (!rc)
427 		printf("### multi:PASS\n");
428 	else
429 		printf("### multi:FAIL\n");
430 	return rc;
431 }
432 
main(int argc,char ** argv)433 int main(int argc, char **argv)
434 {
435 	int rc = 0;
436 
437 	rc = test_foo_bar();
438 	if (rc)
439 		return rc;
440 
441 	return test_multiprog();
442 }
443