GNU Linux-libre 6.9.1-gnu
[releases.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/bpf.h>
18 #include <bpf/btf.h>
19
20 #include "main.h"
21
22 #define HELP_SPEC_ATTACH_FLAGS                                          \
23         "ATTACH_FLAGS := { multi | override }"
24
25 #define HELP_SPEC_ATTACH_TYPES                                          \
26         "       ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
27         "                        cgroup_inet_sock_create | cgroup_sock_ops |\n" \
28         "                        cgroup_device | cgroup_inet4_bind |\n" \
29         "                        cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \
30         "                        cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \
31         "                        cgroup_inet6_connect | cgroup_unix_connect |\n" \
32         "                        cgroup_inet4_getpeername | cgroup_inet6_getpeername |\n" \
33         "                        cgroup_unix_getpeername | cgroup_inet4_getsockname |\n" \
34         "                        cgroup_inet6_getsockname | cgroup_unix_getsockname |\n" \
35         "                        cgroup_udp4_sendmsg | cgroup_udp6_sendmsg |\n" \
36         "                        cgroup_unix_sendmsg | cgroup_udp4_recvmsg |\n" \
37         "                        cgroup_udp6_recvmsg | cgroup_unix_recvmsg |\n" \
38         "                        cgroup_sysctl | cgroup_getsockopt |\n" \
39         "                        cgroup_setsockopt | cgroup_inet_sock_release }"
40
41 static unsigned int query_flags;
42 static struct btf *btf_vmlinux;
43 static __u32 btf_vmlinux_id;
44
45 static enum bpf_attach_type parse_attach_type(const char *str)
46 {
47         const char *attach_type_str;
48         enum bpf_attach_type type;
49
50         for (type = 0; ; type++) {
51                 attach_type_str = libbpf_bpf_attach_type_str(type);
52                 if (!attach_type_str)
53                         break;
54                 if (!strcmp(str, attach_type_str))
55                         return type;
56         }
57
58         /* Also check traditionally used attach type strings. For these we keep
59          * allowing prefixed usage.
60          */
61         for (type = 0; ; type++) {
62                 attach_type_str = bpf_attach_type_input_str(type);
63                 if (!attach_type_str)
64                         break;
65                 if (is_prefix(str, attach_type_str))
66                         return type;
67         }
68
69         return __MAX_BPF_ATTACH_TYPE;
70 }
71
72 static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)
73 {
74         struct bpf_btf_info btf_info = {};
75         __u32 btf_len = sizeof(btf_info);
76         char name[16] = {};
77         int err;
78         int fd;
79
80         btf_info.name = ptr_to_u64(name);
81         btf_info.name_len = sizeof(name);
82
83         fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);
84         if (fd < 0)
85                 return;
86
87         err = bpf_btf_get_info_by_fd(fd, &btf_info, &btf_len);
88         if (err)
89                 goto out;
90
91         if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)
92                 btf_vmlinux_id = btf_info.id;
93
94 out:
95         close(fd);
96 }
97
98 static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
99                          const char *attach_flags_str,
100                          int level)
101 {
102         char prog_name[MAX_PROG_FULL_NAME];
103         const char *attach_btf_name = NULL;
104         struct bpf_prog_info info = {};
105         const char *attach_type_str;
106         __u32 info_len = sizeof(info);
107         int prog_fd;
108
109         prog_fd = bpf_prog_get_fd_by_id(id);
110         if (prog_fd < 0)
111                 return -1;
112
113         if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) {
114                 close(prog_fd);
115                 return -1;
116         }
117
118         attach_type_str = libbpf_bpf_attach_type_str(attach_type);
119
120         if (btf_vmlinux) {
121                 if (!btf_vmlinux_id)
122                         guess_vmlinux_btf_id(info.attach_btf_obj_id);
123
124                 if (btf_vmlinux_id == info.attach_btf_obj_id &&
125                     info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {
126                         const struct btf_type *t =
127                                 btf__type_by_id(btf_vmlinux, info.attach_btf_id);
128                         attach_btf_name =
129                                 btf__name_by_offset(btf_vmlinux, t->name_off);
130                 }
131         }
132
133         get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
134         if (json_output) {
135                 jsonw_start_object(json_wtr);
136                 jsonw_uint_field(json_wtr, "id", info.id);
137                 if (attach_type_str)
138                         jsonw_string_field(json_wtr, "attach_type", attach_type_str);
139                 else
140                         jsonw_uint_field(json_wtr, "attach_type", attach_type);
141                 if (!(query_flags & BPF_F_QUERY_EFFECTIVE))
142                         jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);
143                 jsonw_string_field(json_wtr, "name", prog_name);
144                 if (attach_btf_name)
145                         jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
146                 jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);
147                 jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);
148                 jsonw_end_object(json_wtr);
149         } else {
150                 printf("%s%-8u ", level ? "    " : "", info.id);
151                 if (attach_type_str)
152                         printf("%-15s", attach_type_str);
153                 else
154                         printf("type %-10u", attach_type);
155                 if (query_flags & BPF_F_QUERY_EFFECTIVE)
156                         printf(" %-15s", prog_name);
157                 else
158                         printf(" %-15s %-15s", attach_flags_str, prog_name);
159                 if (attach_btf_name)
160                         printf(" %-15s", attach_btf_name);
161                 else if (info.attach_btf_id)
162                         printf(" attach_btf_obj_id=%d attach_btf_id=%d",
163                                info.attach_btf_obj_id, info.attach_btf_id);
164                 printf("\n");
165         }
166
167         close(prog_fd);
168         return 0;
169 }
170
171 static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
172 {
173         __u32 prog_cnt = 0;
174         int ret;
175
176         ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
177                              NULL, &prog_cnt);
178         if (ret)
179                 return -1;
180
181         return prog_cnt;
182 }
183
184 static int cgroup_has_attached_progs(int cgroup_fd)
185 {
186         enum bpf_attach_type type;
187         bool no_prog = true;
188
189         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
190                 int count = count_attached_bpf_progs(cgroup_fd, type);
191
192                 if (count < 0 && errno != EINVAL)
193                         return -1;
194
195                 if (count > 0) {
196                         no_prog = false;
197                         break;
198                 }
199         }
200
201         return no_prog ? 0 : 1;
202 }
203
204 static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
205                                     int level)
206 {
207         LIBBPF_OPTS(bpf_prog_query_opts, p);
208         __u32 prog_ids[1024] = {0};
209         __u32 iter;
210         int ret;
211
212         p.query_flags = query_flags;
213         p.prog_cnt = ARRAY_SIZE(prog_ids);
214         p.prog_ids = prog_ids;
215
216         ret = bpf_prog_query_opts(cgroup_fd, type, &p);
217         if (ret)
218                 return ret;
219
220         if (p.prog_cnt == 0)
221                 return 0;
222
223         for (iter = 0; iter < p.prog_cnt; iter++)
224                 show_bpf_prog(prog_ids[iter], type, NULL, level);
225
226         return 0;
227 }
228
229 static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
230                                    int level)
231 {
232         LIBBPF_OPTS(bpf_prog_query_opts, p);
233         __u32 prog_attach_flags[1024] = {0};
234         const char *attach_flags_str;
235         __u32 prog_ids[1024] = {0};
236         char buf[32];
237         __u32 iter;
238         int ret;
239
240         p.query_flags = query_flags;
241         p.prog_cnt = ARRAY_SIZE(prog_ids);
242         p.prog_ids = prog_ids;
243         p.prog_attach_flags = prog_attach_flags;
244
245         ret = bpf_prog_query_opts(cgroup_fd, type, &p);
246         if (ret)
247                 return ret;
248
249         if (p.prog_cnt == 0)
250                 return 0;
251
252         for (iter = 0; iter < p.prog_cnt; iter++) {
253                 __u32 attach_flags;
254
255                 attach_flags = prog_attach_flags[iter] ?: p.attach_flags;
256
257                 switch (attach_flags) {
258                 case BPF_F_ALLOW_MULTI:
259                         attach_flags_str = "multi";
260                         break;
261                 case BPF_F_ALLOW_OVERRIDE:
262                         attach_flags_str = "override";
263                         break;
264                 case 0:
265                         attach_flags_str = "";
266                         break;
267                 default:
268                         snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
269                         attach_flags_str = buf;
270                 }
271
272                 show_bpf_prog(prog_ids[iter], type,
273                               attach_flags_str, level);
274         }
275
276         return 0;
277 }
278
279 static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
280                           int level)
281 {
282         return query_flags & BPF_F_QUERY_EFFECTIVE ?
283                show_effective_bpf_progs(cgroup_fd, type, level) :
284                show_attached_bpf_progs(cgroup_fd, type, level);
285 }
286
287 static int do_show(int argc, char **argv)
288 {
289         enum bpf_attach_type type;
290         int has_attached_progs;
291         const char *path;
292         int cgroup_fd;
293         int ret = -1;
294
295         query_flags = 0;
296
297         if (!REQ_ARGS(1))
298                 return -1;
299         path = GET_ARG();
300
301         while (argc) {
302                 if (is_prefix(*argv, "effective")) {
303                         if (query_flags & BPF_F_QUERY_EFFECTIVE) {
304                                 p_err("duplicated argument: %s", *argv);
305                                 return -1;
306                         }
307                         query_flags |= BPF_F_QUERY_EFFECTIVE;
308                         NEXT_ARG();
309                 } else {
310                         p_err("expected no more arguments, 'effective', got: '%s'?",
311                               *argv);
312                         return -1;
313                 }
314         }
315
316         cgroup_fd = open(path, O_RDONLY);
317         if (cgroup_fd < 0) {
318                 p_err("can't open cgroup %s", path);
319                 goto exit;
320         }
321
322         has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
323         if (has_attached_progs < 0) {
324                 p_err("can't query bpf programs attached to %s: %s",
325                       path, strerror(errno));
326                 goto exit_cgroup;
327         } else if (!has_attached_progs) {
328                 ret = 0;
329                 goto exit_cgroup;
330         }
331
332         if (json_output)
333                 jsonw_start_array(json_wtr);
334         else if (query_flags & BPF_F_QUERY_EFFECTIVE)
335                 printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");
336         else
337                 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
338                        "AttachFlags", "Name");
339
340         btf_vmlinux = libbpf_find_kernel_btf();
341         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
342                 /*
343                  * Not all attach types may be supported, so it's expected,
344                  * that some requests will fail.
345                  * If we were able to get the show for at least one
346                  * attach type, let's return 0.
347                  */
348                 if (show_bpf_progs(cgroup_fd, type, 0) == 0)
349                         ret = 0;
350         }
351
352         if (json_output)
353                 jsonw_end_array(json_wtr);
354
355 exit_cgroup:
356         close(cgroup_fd);
357 exit:
358         return ret;
359 }
360
361 /*
362  * To distinguish nftw() errors and do_show_tree_fn() errors
363  * and avoid duplicating error messages, let's return -2
364  * from do_show_tree_fn() in case of error.
365  */
366 #define NFTW_ERR                -1
367 #define SHOW_TREE_FN_ERR        -2
368 static int do_show_tree_fn(const char *fpath, const struct stat *sb,
369                            int typeflag, struct FTW *ftw)
370 {
371         enum bpf_attach_type type;
372         int has_attached_progs;
373         int cgroup_fd;
374
375         if (typeflag != FTW_D)
376                 return 0;
377
378         cgroup_fd = open(fpath, O_RDONLY);
379         if (cgroup_fd < 0) {
380                 p_err("can't open cgroup %s: %s", fpath, strerror(errno));
381                 return SHOW_TREE_FN_ERR;
382         }
383
384         has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
385         if (has_attached_progs < 0) {
386                 p_err("can't query bpf programs attached to %s: %s",
387                       fpath, strerror(errno));
388                 close(cgroup_fd);
389                 return SHOW_TREE_FN_ERR;
390         } else if (!has_attached_progs) {
391                 close(cgroup_fd);
392                 return 0;
393         }
394
395         if (json_output) {
396                 jsonw_start_object(json_wtr);
397                 jsonw_string_field(json_wtr, "cgroup", fpath);
398                 jsonw_name(json_wtr, "programs");
399                 jsonw_start_array(json_wtr);
400         } else {
401                 printf("%s\n", fpath);
402         }
403
404         btf_vmlinux = libbpf_find_kernel_btf();
405         for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
406                 show_bpf_progs(cgroup_fd, type, ftw->level);
407
408         if (errno == EINVAL)
409                 /* Last attach type does not support query.
410                  * Do not report an error for this, especially because batch
411                  * mode would stop processing commands.
412                  */
413                 errno = 0;
414
415         if (json_output) {
416                 jsonw_end_array(json_wtr);
417                 jsonw_end_object(json_wtr);
418         }
419
420         close(cgroup_fd);
421
422         return 0;
423 }
424
425 static char *find_cgroup_root(void)
426 {
427         struct mntent *mnt;
428         FILE *f;
429
430         f = fopen("/proc/mounts", "r");
431         if (f == NULL)
432                 return NULL;
433
434         while ((mnt = getmntent(f))) {
435                 if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
436                         fclose(f);
437                         return strdup(mnt->mnt_dir);
438                 }
439         }
440
441         fclose(f);
442         return NULL;
443 }
444
445 static int do_show_tree(int argc, char **argv)
446 {
447         char *cgroup_root, *cgroup_alloced = NULL;
448         int ret;
449
450         query_flags = 0;
451
452         if (!argc) {
453                 cgroup_alloced = find_cgroup_root();
454                 if (!cgroup_alloced) {
455                         p_err("cgroup v2 isn't mounted");
456                         return -1;
457                 }
458                 cgroup_root = cgroup_alloced;
459         } else {
460                 cgroup_root = GET_ARG();
461
462                 while (argc) {
463                         if (is_prefix(*argv, "effective")) {
464                                 if (query_flags & BPF_F_QUERY_EFFECTIVE) {
465                                         p_err("duplicated argument: %s", *argv);
466                                         return -1;
467                                 }
468                                 query_flags |= BPF_F_QUERY_EFFECTIVE;
469                                 NEXT_ARG();
470                         } else {
471                                 p_err("expected no more arguments, 'effective', got: '%s'?",
472                                       *argv);
473                                 return -1;
474                         }
475                 }
476         }
477
478         if (json_output)
479                 jsonw_start_array(json_wtr);
480         else if (query_flags & BPF_F_QUERY_EFFECTIVE)
481                 printf("%s\n"
482                        "%-8s %-15s %-15s\n",
483                        "CgroupPath",
484                        "ID", "AttachType", "Name");
485         else
486                 printf("%s\n"
487                        "%-8s %-15s %-15s %-15s\n",
488                        "CgroupPath",
489                        "ID", "AttachType", "AttachFlags", "Name");
490
491         switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
492         case NFTW_ERR:
493                 p_err("can't iterate over %s: %s", cgroup_root,
494                       strerror(errno));
495                 ret = -1;
496                 break;
497         case SHOW_TREE_FN_ERR:
498                 ret = -1;
499                 break;
500         default:
501                 ret = 0;
502         }
503
504         if (json_output)
505                 jsonw_end_array(json_wtr);
506
507         free(cgroup_alloced);
508
509         return ret;
510 }
511
512 static int do_attach(int argc, char **argv)
513 {
514         enum bpf_attach_type attach_type;
515         int cgroup_fd, prog_fd;
516         int attach_flags = 0;
517         int ret = -1;
518         int i;
519
520         if (argc < 4) {
521                 p_err("too few parameters for cgroup attach");
522                 goto exit;
523         }
524
525         cgroup_fd = open(argv[0], O_RDONLY);
526         if (cgroup_fd < 0) {
527                 p_err("can't open cgroup %s", argv[0]);
528                 goto exit;
529         }
530
531         attach_type = parse_attach_type(argv[1]);
532         if (attach_type == __MAX_BPF_ATTACH_TYPE) {
533                 p_err("invalid attach type");
534                 goto exit_cgroup;
535         }
536
537         argc -= 2;
538         argv = &argv[2];
539         prog_fd = prog_parse_fd(&argc, &argv);
540         if (prog_fd < 0)
541                 goto exit_cgroup;
542
543         for (i = 0; i < argc; i++) {
544                 if (is_prefix(argv[i], "multi")) {
545                         attach_flags |= BPF_F_ALLOW_MULTI;
546                 } else if (is_prefix(argv[i], "override")) {
547                         attach_flags |= BPF_F_ALLOW_OVERRIDE;
548                 } else {
549                         p_err("unknown option: %s", argv[i]);
550                         goto exit_cgroup;
551                 }
552         }
553
554         if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
555                 p_err("failed to attach program");
556                 goto exit_prog;
557         }
558
559         if (json_output)
560                 jsonw_null(json_wtr);
561
562         ret = 0;
563
564 exit_prog:
565         close(prog_fd);
566 exit_cgroup:
567         close(cgroup_fd);
568 exit:
569         return ret;
570 }
571
572 static int do_detach(int argc, char **argv)
573 {
574         enum bpf_attach_type attach_type;
575         int prog_fd, cgroup_fd;
576         int ret = -1;
577
578         if (argc < 4) {
579                 p_err("too few parameters for cgroup detach");
580                 goto exit;
581         }
582
583         cgroup_fd = open(argv[0], O_RDONLY);
584         if (cgroup_fd < 0) {
585                 p_err("can't open cgroup %s", argv[0]);
586                 goto exit;
587         }
588
589         attach_type = parse_attach_type(argv[1]);
590         if (attach_type == __MAX_BPF_ATTACH_TYPE) {
591                 p_err("invalid attach type");
592                 goto exit_cgroup;
593         }
594
595         argc -= 2;
596         argv = &argv[2];
597         prog_fd = prog_parse_fd(&argc, &argv);
598         if (prog_fd < 0)
599                 goto exit_cgroup;
600
601         if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
602                 p_err("failed to detach program");
603                 goto exit_prog;
604         }
605
606         if (json_output)
607                 jsonw_null(json_wtr);
608
609         ret = 0;
610
611 exit_prog:
612         close(prog_fd);
613 exit_cgroup:
614         close(cgroup_fd);
615 exit:
616         return ret;
617 }
618
619 static int do_help(int argc, char **argv)
620 {
621         if (json_output) {
622                 jsonw_null(json_wtr);
623                 return 0;
624         }
625
626         fprintf(stderr,
627                 "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
628                 "       %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
629                 "       %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
630                 "       %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
631                 "       %1$s %2$s help\n"
632                 "\n"
633                 HELP_SPEC_ATTACH_TYPES "\n"
634                 "       " HELP_SPEC_ATTACH_FLAGS "\n"
635                 "       " HELP_SPEC_PROGRAM "\n"
636                 "       " HELP_SPEC_OPTIONS " |\n"
637                 "                    {-f|--bpffs} }\n"
638                 "",
639                 bin_name, argv[-2]);
640
641         return 0;
642 }
643
644 static const struct cmd cmds[] = {
645         { "show",       do_show },
646         { "list",       do_show },
647         { "tree",       do_show_tree },
648         { "attach",     do_attach },
649         { "detach",     do_detach },
650         { "help",       do_help },
651         { 0 }
652 };
653
654 int do_cgroup(int argc, char **argv)
655 {
656         return cmd_select(cmds, argc, argv, do_help);
657 }