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