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