Linux Audio

Check our new training course

Yocto distribution development and maintenance

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