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