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