Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / tools / bpf / bpftool / cgroup.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 // Copyright (C) 2017 Facebook
3 // Author: Roman Gushchin <guro@fb.com>
4
5 #define _XOPEN_SOURCE 500
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <ftw.h>
9 #include <mntent.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16
17 #include <bpf.h>
18
19 #include "main.h"
20
21 #define HELP_SPEC_ATTACH_FLAGS                                          \
22         "ATTACH_FLAGS := { multi | override }"
23
24 #define HELP_SPEC_ATTACH_TYPES                                                 \
25         "       ATTACH_TYPE := { ingress | egress | sock_create |\n"           \
26         "                        sock_ops | device | bind4 | bind6 |\n"        \
27         "                        post_bind4 | post_bind6 | connect4 |\n"       \
28         "                        connect6 | sendmsg4 | sendmsg6 |\n"           \
29         "                        recvmsg4 | recvmsg6 | sysctl |\n"             \
30         "                        getsockopt | setsockopt }"
31
32 static const char * const attach_type_strings[] = {
33         [BPF_CGROUP_INET_INGRESS] = "ingress",
34         [BPF_CGROUP_INET_EGRESS] = "egress",
35         [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
36         [BPF_CGROUP_SOCK_OPS] = "sock_ops",
37         [BPF_CGROUP_DEVICE] = "device",
38         [BPF_CGROUP_INET4_BIND] = "bind4",
39         [BPF_CGROUP_INET6_BIND] = "bind6",
40         [BPF_CGROUP_INET4_CONNECT] = "connect4",
41         [BPF_CGROUP_INET6_CONNECT] = "connect6",
42         [BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
43         [BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
44         [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
45         [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
46         [BPF_CGROUP_SYSCTL] = "sysctl",
47         [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4",
48         [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6",
49         [BPF_CGROUP_GETSOCKOPT] = "getsockopt",
50         [BPF_CGROUP_SETSOCKOPT] = "setsockopt",
51         [__MAX_BPF_ATTACH_TYPE] = NULL,
52 };
53
54 static enum bpf_attach_type parse_attach_type(const char *str)
55 {
56         enum bpf_attach_type type;
57
58         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
59                 if (attach_type_strings[type] &&
60                     is_prefix(str, attach_type_strings[type]))
61                         return type;
62         }
63
64         return __MAX_BPF_ATTACH_TYPE;
65 }
66
67 static int show_bpf_prog(int id, const char *attach_type_str,
68                          const char *attach_flags_str,
69                          int level)
70 {
71         struct bpf_prog_info info = {};
72         __u32 info_len = sizeof(info);
73         int prog_fd;
74
75         prog_fd = bpf_prog_get_fd_by_id(id);
76         if (prog_fd < 0)
77                 return -1;
78
79         if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
80                 close(prog_fd);
81                 return -1;
82         }
83
84         if (json_output) {
85                 jsonw_start_object(json_wtr);
86                 jsonw_uint_field(json_wtr, "id", info.id);
87                 jsonw_string_field(json_wtr, "attach_type",
88                                    attach_type_str);
89                 jsonw_string_field(json_wtr, "attach_flags",
90                                    attach_flags_str);
91                 jsonw_string_field(json_wtr, "name", info.name);
92                 jsonw_end_object(json_wtr);
93         } else {
94                 printf("%s%-8u %-15s %-15s %-15s\n", level ? "    " : "",
95                        info.id,
96                        attach_type_str,
97                        attach_flags_str,
98                        info.name);
99         }
100
101         close(prog_fd);
102         return 0;
103 }
104
105 static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
106 {
107         __u32 prog_cnt = 0;
108         int ret;
109
110         ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt);
111         if (ret)
112                 return -1;
113
114         return prog_cnt;
115 }
116
117 static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
118                                    int level)
119 {
120         __u32 prog_ids[1024] = {0};
121         char *attach_flags_str;
122         __u32 prog_cnt, iter;
123         __u32 attach_flags;
124         char buf[32];
125         int ret;
126
127         prog_cnt = ARRAY_SIZE(prog_ids);
128         ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids,
129                              &prog_cnt);
130         if (ret)
131                 return ret;
132
133         if (prog_cnt == 0)
134                 return 0;
135
136         switch (attach_flags) {
137         case BPF_F_ALLOW_MULTI:
138                 attach_flags_str = "multi";
139                 break;
140         case BPF_F_ALLOW_OVERRIDE:
141                 attach_flags_str = "override";
142                 break;
143         case 0:
144                 attach_flags_str = "";
145                 break;
146         default:
147                 snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
148                 attach_flags_str = buf;
149         }
150
151         for (iter = 0; iter < prog_cnt; iter++)
152                 show_bpf_prog(prog_ids[iter], attach_type_strings[type],
153                               attach_flags_str, level);
154
155         return 0;
156 }
157
158 static int do_show(int argc, char **argv)
159 {
160         enum bpf_attach_type type;
161         int cgroup_fd;
162         int ret = -1;
163
164         if (argc < 1) {
165                 p_err("too few parameters for cgroup show");
166                 goto exit;
167         } else if (argc > 1) {
168                 p_err("too many parameters for cgroup show");
169                 goto exit;
170         }
171
172         cgroup_fd = open(argv[0], O_RDONLY);
173         if (cgroup_fd < 0) {
174                 p_err("can't open cgroup %s", argv[0]);
175                 goto exit;
176         }
177
178         if (json_output)
179                 jsonw_start_array(json_wtr);
180         else
181                 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
182                        "AttachFlags", "Name");
183
184         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
185                 /*
186                  * Not all attach types may be supported, so it's expected,
187                  * that some requests will fail.
188                  * If we were able to get the show for at least one
189                  * attach type, let's return 0.
190                  */
191                 if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
192                         ret = 0;
193         }
194
195         if (json_output)
196                 jsonw_end_array(json_wtr);
197
198         close(cgroup_fd);
199 exit:
200         return ret;
201 }
202
203 /*
204  * To distinguish nftw() errors and do_show_tree_fn() errors
205  * and avoid duplicating error messages, let's return -2
206  * from do_show_tree_fn() in case of error.
207  */
208 #define NFTW_ERR                -1
209 #define SHOW_TREE_FN_ERR        -2
210 static int do_show_tree_fn(const char *fpath, const struct stat *sb,
211                            int typeflag, struct FTW *ftw)
212 {
213         enum bpf_attach_type type;
214         bool skip = true;
215         int cgroup_fd;
216
217         if (typeflag != FTW_D)
218                 return 0;
219
220         cgroup_fd = open(fpath, O_RDONLY);
221         if (cgroup_fd < 0) {
222                 p_err("can't open cgroup %s: %s", fpath, strerror(errno));
223                 return SHOW_TREE_FN_ERR;
224         }
225
226         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
227                 int count = count_attached_bpf_progs(cgroup_fd, type);
228
229                 if (count < 0 && errno != EINVAL) {
230                         p_err("can't query bpf programs attached to %s: %s",
231                               fpath, strerror(errno));
232                         close(cgroup_fd);
233                         return SHOW_TREE_FN_ERR;
234                 }
235                 if (count > 0) {
236                         skip = false;
237                         break;
238                 }
239         }
240
241         if (skip) {
242                 close(cgroup_fd);
243                 return 0;
244         }
245
246         if (json_output) {
247                 jsonw_start_object(json_wtr);
248                 jsonw_string_field(json_wtr, "cgroup", fpath);
249                 jsonw_name(json_wtr, "programs");
250                 jsonw_start_array(json_wtr);
251         } else {
252                 printf("%s\n", fpath);
253         }
254
255         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
256                 show_attached_bpf_progs(cgroup_fd, type, ftw->level);
257
258         if (errno == EINVAL)
259                 /* Last attach type does not support query.
260                  * Do not report an error for this, especially because batch
261                  * mode would stop processing commands.
262                  */
263                 errno = 0;
264
265         if (json_output) {
266                 jsonw_end_array(json_wtr);
267                 jsonw_end_object(json_wtr);
268         }
269
270         close(cgroup_fd);
271
272         return 0;
273 }
274
275 static char *find_cgroup_root(void)
276 {
277         struct mntent *mnt;
278         FILE *f;
279
280         f = fopen("/proc/mounts", "r");
281         if (f == NULL)
282                 return NULL;
283
284         while ((mnt = getmntent(f))) {
285                 if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
286                         fclose(f);
287                         return strdup(mnt->mnt_dir);
288                 }
289         }
290
291         fclose(f);
292         return NULL;
293 }
294
295 static int do_show_tree(int argc, char **argv)
296 {
297         char *cgroup_root;
298         int ret;
299
300         switch (argc) {
301         case 0:
302                 cgroup_root = find_cgroup_root();
303                 if (!cgroup_root) {
304                         p_err("cgroup v2 isn't mounted");
305                         return -1;
306                 }
307                 break;
308         case 1:
309                 cgroup_root = argv[0];
310                 break;
311         default:
312                 p_err("too many parameters for cgroup tree");
313                 return -1;
314         }
315
316
317         if (json_output)
318                 jsonw_start_array(json_wtr);
319         else
320                 printf("%s\n"
321                        "%-8s %-15s %-15s %-15s\n",
322                        "CgroupPath",
323                        "ID", "AttachType", "AttachFlags", "Name");
324
325         switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
326         case NFTW_ERR:
327                 p_err("can't iterate over %s: %s", cgroup_root,
328                       strerror(errno));
329                 ret = -1;
330                 break;
331         case SHOW_TREE_FN_ERR:
332                 ret = -1;
333                 break;
334         default:
335                 ret = 0;
336         }
337
338         if (json_output)
339                 jsonw_end_array(json_wtr);
340
341         if (argc == 0)
342                 free(cgroup_root);
343
344         return ret;
345 }
346
347 static int do_attach(int argc, char **argv)
348 {
349         enum bpf_attach_type attach_type;
350         int cgroup_fd, prog_fd;
351         int attach_flags = 0;
352         int ret = -1;
353         int i;
354
355         if (argc < 4) {
356                 p_err("too few parameters for cgroup attach");
357                 goto exit;
358         }
359
360         cgroup_fd = open(argv[0], O_RDONLY);
361         if (cgroup_fd < 0) {
362                 p_err("can't open cgroup %s", argv[0]);
363                 goto exit;
364         }
365
366         attach_type = parse_attach_type(argv[1]);
367         if (attach_type == __MAX_BPF_ATTACH_TYPE) {
368                 p_err("invalid attach type");
369                 goto exit_cgroup;
370         }
371
372         argc -= 2;
373         argv = &argv[2];
374         prog_fd = prog_parse_fd(&argc, &argv);
375         if (prog_fd < 0)
376                 goto exit_cgroup;
377
378         for (i = 0; i < argc; i++) {
379                 if (is_prefix(argv[i], "multi")) {
380                         attach_flags |= BPF_F_ALLOW_MULTI;
381                 } else if (is_prefix(argv[i], "override")) {
382                         attach_flags |= BPF_F_ALLOW_OVERRIDE;
383                 } else {
384                         p_err("unknown option: %s", argv[i]);
385                         goto exit_cgroup;
386                 }
387         }
388
389         if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
390                 p_err("failed to attach program");
391                 goto exit_prog;
392         }
393
394         if (json_output)
395                 jsonw_null(json_wtr);
396
397         ret = 0;
398
399 exit_prog:
400         close(prog_fd);
401 exit_cgroup:
402         close(cgroup_fd);
403 exit:
404         return ret;
405 }
406
407 static int do_detach(int argc, char **argv)
408 {
409         enum bpf_attach_type attach_type;
410         int prog_fd, cgroup_fd;
411         int ret = -1;
412
413         if (argc < 4) {
414                 p_err("too few parameters for cgroup detach");
415                 goto exit;
416         }
417
418         cgroup_fd = open(argv[0], O_RDONLY);
419         if (cgroup_fd < 0) {
420                 p_err("can't open cgroup %s", argv[0]);
421                 goto exit;
422         }
423
424         attach_type = parse_attach_type(argv[1]);
425         if (attach_type == __MAX_BPF_ATTACH_TYPE) {
426                 p_err("invalid attach type");
427                 goto exit_cgroup;
428         }
429
430         argc -= 2;
431         argv = &argv[2];
432         prog_fd = prog_parse_fd(&argc, &argv);
433         if (prog_fd < 0)
434                 goto exit_cgroup;
435
436         if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
437                 p_err("failed to detach program");
438                 goto exit_prog;
439         }
440
441         if (json_output)
442                 jsonw_null(json_wtr);
443
444         ret = 0;
445
446 exit_prog:
447         close(prog_fd);
448 exit_cgroup:
449         close(cgroup_fd);
450 exit:
451         return ret;
452 }
453
454 static int do_help(int argc, char **argv)
455 {
456         if (json_output) {
457                 jsonw_null(json_wtr);
458                 return 0;
459         }
460
461         fprintf(stderr,
462                 "Usage: %s %s { show | list } CGROUP\n"
463                 "       %s %s tree [CGROUP_ROOT]\n"
464                 "       %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
465                 "       %s %s detach CGROUP ATTACH_TYPE PROG\n"
466                 "       %s %s help\n"
467                 "\n"
468                 HELP_SPEC_ATTACH_TYPES "\n"
469                 "       " HELP_SPEC_ATTACH_FLAGS "\n"
470                 "       " HELP_SPEC_PROGRAM "\n"
471                 "       " HELP_SPEC_OPTIONS "\n"
472                 "",
473                 bin_name, argv[-2],
474                 bin_name, argv[-2], bin_name, argv[-2],
475                 bin_name, argv[-2], bin_name, argv[-2]);
476
477         return 0;
478 }
479
480 static const struct cmd cmds[] = {
481         { "show",       do_show },
482         { "list",       do_show },
483         { "tree",       do_show_tree },
484         { "attach",     do_attach },
485         { "detach",     do_detach },
486         { "help",       do_help },
487         { 0 }
488 };
489
490 int do_cgroup(int argc, char **argv)
491 {
492         return cmd_select(cmds, argc, argv, do_help);
493 }