Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/bpf/bpftool/cgroup.c
29267 views
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
#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
26
static 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
77
static unsigned int query_flags;
78
static struct btf *btf_vmlinux;
79
static __u32 btf_vmlinux_id;
80
81
static 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
108
static 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
130
out:
131
close(fd);
132
}
133
134
static 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
207
static 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
220
static 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
240
static 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
265
static 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
315
static 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
323
static 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
391
exit_cgroup:
392
close(cgroup_fd);
393
exit:
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
404
static 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
461
static 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
481
static 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
548
static 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
600
exit_prog:
601
close(prog_fd);
602
exit_cgroup:
603
close(cgroup_fd);
604
exit:
605
return ret;
606
}
607
608
static 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
647
exit_prog:
648
close(prog_fd);
649
exit_cgroup:
650
close(cgroup_fd);
651
exit:
652
return ret;
653
}
654
655
static 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
680
static 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
690
int do_cgroup(int argc, char **argv)
691
{
692
return cmd_select(cmds, argc, argv, do_help);
693
}
694
695