Merge tag 'nfs-for-4.15-2' of git://git.linux-nfs.org/projects/anna/linux-nfs
[linux-2.6-microblaze.git] / samples / bpf / test_cgrp2_attach2.c
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
35 char bpf_log_buf[BPF_LOG_BUF_SIZE];
36
37 static 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
58 static 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, 1)) {
82                 log_err("Attaching prog to /foo");
83                 goto err;
84         }
85
86         printf("Attached DROP prog. This ping in cgroup /foo should fail...\n");
87         assert(system(PING_CMD) != 0);
88
89         /* Create cgroup /foo/bar, get fd, and join it */
90         bar = create_and_get_cgroup(BAR);
91         if (!bar)
92                 goto err;
93
94         if (join_cgroup(BAR))
95                 goto err;
96
97         printf("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
98         assert(system(PING_CMD) != 0);
99
100         if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) {
101                 log_err("Attaching prog to /foo/bar");
102                 goto err;
103         }
104
105         printf("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
106         assert(system(PING_CMD) == 0);
107
108         if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
109                 log_err("Detaching program from /foo/bar");
110                 goto err;
111         }
112
113         printf("Detached PASS from /foo/bar while DROP is attached to /foo.\n"
114                "This ping in cgroup /foo/bar should fail...\n");
115         assert(system(PING_CMD) != 0);
116
117         if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) {
118                 log_err("Attaching prog to /foo/bar");
119                 goto err;
120         }
121
122         if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
123                 log_err("Detaching program from /foo");
124                 goto err;
125         }
126
127         printf("Attached PASS from /foo/bar and detached DROP from /foo.\n"
128                "This ping in cgroup /foo/bar should pass...\n");
129         assert(system(PING_CMD) == 0);
130
131         if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) {
132                 log_err("Attaching prog to /foo/bar");
133                 goto err;
134         }
135
136         if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
137                 errno = 0;
138                 log_err("Unexpected success attaching prog to /foo/bar");
139                 goto err;
140         }
141
142         if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
143                 log_err("Detaching program from /foo/bar");
144                 goto err;
145         }
146
147         if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
148                 errno = 0;
149                 log_err("Unexpected success in double detach from /foo");
150                 goto err;
151         }
152
153         if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
154                 log_err("Attaching non-overridable prog to /foo");
155                 goto err;
156         }
157
158         if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
159                 errno = 0;
160                 log_err("Unexpected success attaching non-overridable prog to /foo/bar");
161                 goto err;
162         }
163
164         if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 1)) {
165                 errno = 0;
166                 log_err("Unexpected success attaching overridable prog to /foo/bar");
167                 goto err;
168         }
169
170         if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 1)) {
171                 errno = 0;
172                 log_err("Unexpected success attaching overridable prog to /foo");
173                 goto err;
174         }
175
176         if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
177                 log_err("Attaching different non-overridable prog to /foo");
178                 goto err;
179         }
180
181         goto out;
182
183 err:
184         rc = 1;
185
186 out:
187         close(foo);
188         close(bar);
189         cleanup_cgroup_environment();
190         if (!rc)
191                 printf("### override:PASS\n");
192         else
193                 printf("### override:FAIL\n");
194         return rc;
195 }
196
197 static int map_fd = -1;
198
199 static int prog_load_cnt(int verdict, int val)
200 {
201         if (map_fd < 0)
202                 map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0);
203         if (map_fd < 0) {
204                 printf("failed to create map '%s'\n", strerror(errno));
205                 return -1;
206         }
207
208         struct bpf_insn prog[] = {
209                 BPF_MOV32_IMM(BPF_REG_0, 0),
210                 BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
211                 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
212                 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
213                 BPF_LD_MAP_FD(BPF_REG_1, map_fd),
214                 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
215                 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
216                 BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */
217                 BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
218                 BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
219                 BPF_EXIT_INSN(),
220         };
221         size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
222         int ret;
223
224         ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
225                                prog, insns_cnt, "GPL", 0,
226                                bpf_log_buf, BPF_LOG_BUF_SIZE);
227
228         if (ret < 0) {
229                 log_err("Loading program");
230                 printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
231                 return 0;
232         }
233         return ret;
234 }
235
236
237 static int test_multiprog(void)
238 {
239         __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id;
240         int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0;
241         int drop_prog, allow_prog[6] = {}, rc = 0;
242         unsigned long long value;
243         int i = 0;
244
245         for (i = 0; i < 6; i++) {
246                 allow_prog[i] = prog_load_cnt(1, 1 << i);
247                 if (!allow_prog[i])
248                         goto err;
249         }
250         drop_prog = prog_load_cnt(0, 1);
251         if (!drop_prog)
252                 goto err;
253
254         if (setup_cgroup_environment())
255                 goto err;
256
257         cg1 = create_and_get_cgroup("/cg1");
258         if (!cg1)
259                 goto err;
260         cg2 = create_and_get_cgroup("/cg1/cg2");
261         if (!cg2)
262                 goto err;
263         cg3 = create_and_get_cgroup("/cg1/cg2/cg3");
264         if (!cg3)
265                 goto err;
266         cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4");
267         if (!cg4)
268                 goto err;
269         cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5");
270         if (!cg5)
271                 goto err;
272
273         if (join_cgroup("/cg1/cg2/cg3/cg4/cg5"))
274                 goto err;
275
276         if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, 2)) {
277                 log_err("Attaching prog to cg1");
278                 goto err;
279         }
280         if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, 2)) {
281                 log_err("Unexpected success attaching the same prog to cg1");
282                 goto err;
283         }
284         if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS, 2)) {
285                 log_err("Attaching prog2 to cg1");
286                 goto err;
287         }
288         if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS, 1)) {
289                 log_err("Attaching prog to cg2");
290                 goto err;
291         }
292         if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS, 2)) {
293                 log_err("Attaching prog to cg3");
294                 goto err;
295         }
296         if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS, 1)) {
297                 log_err("Attaching prog to cg4");
298                 goto err;
299         }
300         if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) {
301                 log_err("Attaching prog to cg5");
302                 goto err;
303         }
304         assert(system(PING_CMD) == 0);
305         assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
306         assert(value == 1 + 2 + 8 + 32);
307
308         /* query the number of effective progs in cg5 */
309         assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
310                               NULL, NULL, &prog_cnt) == 0);
311         assert(prog_cnt == 4);
312         /* retrieve prog_ids of effective progs in cg5 */
313         assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
314                               &attach_flags, prog_ids, &prog_cnt) == 0);
315         assert(prog_cnt == 4);
316         assert(attach_flags == 0);
317         saved_prog_id = prog_ids[0];
318         /* check enospc handling */
319         prog_ids[0] = 0;
320         prog_cnt = 2;
321         assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
322                               &attach_flags, prog_ids, &prog_cnt) == -1 &&
323                errno == ENOSPC);
324         assert(prog_cnt == 4);
325         /* check that prog_ids are returned even when buffer is too small */
326         assert(prog_ids[0] == saved_prog_id);
327         /* retrieve prog_id of single attached prog in cg5 */
328         prog_ids[0] = 0;
329         assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
330                               NULL, prog_ids, &prog_cnt) == 0);
331         assert(prog_cnt == 1);
332         assert(prog_ids[0] == saved_prog_id);
333
334         /* detach bottom program and ping again */
335         if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) {
336                 log_err("Detaching prog from cg5");
337                 goto err;
338         }
339         value = 0;
340         assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
341         assert(system(PING_CMD) == 0);
342         assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
343         assert(value == 1 + 2 + 8 + 16);
344
345         /* detach 3rd from bottom program and ping again */
346         errno = 0;
347         if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) {
348                 log_err("Unexpected success on detach from cg3");
349                 goto err;
350         }
351         if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) {
352                 log_err("Detaching from cg3");
353                 goto err;
354         }
355         value = 0;
356         assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
357         assert(system(PING_CMD) == 0);
358         assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
359         assert(value == 1 + 2 + 16);
360
361         /* detach 2nd from bottom program and ping again */
362         if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) {
363                 log_err("Detaching prog from cg4");
364                 goto err;
365         }
366         value = 0;
367         assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
368         assert(system(PING_CMD) == 0);
369         assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
370         assert(value == 1 + 2 + 4);
371
372         prog_cnt = 4;
373         assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
374                               &attach_flags, prog_ids, &prog_cnt) == 0);
375         assert(prog_cnt == 3);
376         assert(attach_flags == 0);
377         assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
378                               NULL, prog_ids, &prog_cnt) == 0);
379         assert(prog_cnt == 0);
380         goto out;
381 err:
382         rc = 1;
383
384 out:
385         for (i = 0; i < 6; i++)
386                 if (allow_prog[i] > 0)
387                         close(allow_prog[i]);
388         close(cg1);
389         close(cg2);
390         close(cg3);
391         close(cg4);
392         close(cg5);
393         cleanup_cgroup_environment();
394         if (!rc)
395                 printf("### multi:PASS\n");
396         else
397                 printf("### multi:FAIL\n");
398         return rc;
399 }
400
401 int main(int argc, char **argv)
402 {
403         int rc = 0;
404
405         rc = test_foo_bar();
406         if (rc)
407                 return rc;
408
409         return test_multiprog();
410 }