Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/bpf/bpftool/cgroup.c
26282 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
#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=%u attach_btf_id=%u",
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 && errno != EINVAL)
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
int has_attached_progs;
322
const char *path;
323
int cgroup_fd;
324
int ret = -1;
325
unsigned int i;
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 (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) {
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, cgroup_attach_types[i], 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
int has_attached_progs;
404
int cgroup_fd;
405
unsigned int i;
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 (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++)
438
show_bpf_progs(cgroup_fd, cgroup_attach_types[i], 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
}
690
691