blob: 7e22f115c8c1af352984961a3a6c0dc336d4b864 [file] [log] [blame]
Jakub Kicinski907b2232018-12-12 19:59:26 -08001// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
Roman Gushchin5ccda642017-12-13 15:18:54 +00002// Copyright (C) 2017 Facebook
3// Author: Roman Gushchin <guro@fb.com>
4
Roman Gushchin2058b382018-07-06 14:28:14 -07005#define _XOPEN_SOURCE 500
6#include <errno.h>
Roman Gushchin5ccda642017-12-13 15:18:54 +00007#include <fcntl.h>
Roman Gushchin2058b382018-07-06 14:28:14 -07008#include <ftw.h>
9#include <mntent.h>
10#include <stdio.h>
Roman Gushchin5ccda642017-12-13 15:18:54 +000011#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.h>
18
19#include "main.h"
20
21#define HELP_SPEC_ATTACH_FLAGS \
22 "ATTACH_FLAGS := { multi | override }"
23
Andrey Ignatov393de512018-04-17 10:28:44 -070024#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" \
Andrey Ignatovf25377e2019-04-16 13:13:47 -070028 " connect6 | sendmsg4 | sendmsg6 | sysctl }"
Roman Gushchin5ccda642017-12-13 15:18:54 +000029
30static const char * const attach_type_strings[] = {
31 [BPF_CGROUP_INET_INGRESS] = "ingress",
32 [BPF_CGROUP_INET_EGRESS] = "egress",
33 [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create",
34 [BPF_CGROUP_SOCK_OPS] = "sock_ops",
35 [BPF_CGROUP_DEVICE] = "device",
Andrey Ignatov393de512018-04-17 10:28:44 -070036 [BPF_CGROUP_INET4_BIND] = "bind4",
37 [BPF_CGROUP_INET6_BIND] = "bind6",
38 [BPF_CGROUP_INET4_CONNECT] = "connect4",
39 [BPF_CGROUP_INET6_CONNECT] = "connect6",
40 [BPF_CGROUP_INET4_POST_BIND] = "post_bind4",
41 [BPF_CGROUP_INET6_POST_BIND] = "post_bind6",
Andrey Ignatov13a370b2018-05-29 13:29:31 -070042 [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4",
43 [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6",
Andrey Ignatovf25377e2019-04-16 13:13:47 -070044 [BPF_CGROUP_SYSCTL] = "sysctl",
Roman Gushchin5ccda642017-12-13 15:18:54 +000045 [__MAX_BPF_ATTACH_TYPE] = NULL,
46};
47
48static enum bpf_attach_type parse_attach_type(const char *str)
49{
50 enum bpf_attach_type type;
51
52 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
53 if (attach_type_strings[type] &&
54 is_prefix(str, attach_type_strings[type]))
55 return type;
56 }
57
58 return __MAX_BPF_ATTACH_TYPE;
59}
60
Jakub Kicinski65b875b2018-01-02 14:48:35 -080061static int show_bpf_prog(int id, const char *attach_type_str,
Roman Gushchin2058b382018-07-06 14:28:14 -070062 const char *attach_flags_str,
63 int level)
Roman Gushchin5ccda642017-12-13 15:18:54 +000064{
65 struct bpf_prog_info info = {};
66 __u32 info_len = sizeof(info);
67 int prog_fd;
68
69 prog_fd = bpf_prog_get_fd_by_id(id);
70 if (prog_fd < 0)
71 return -1;
72
73 if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
74 close(prog_fd);
75 return -1;
76 }
77
78 if (json_output) {
79 jsonw_start_object(json_wtr);
80 jsonw_uint_field(json_wtr, "id", info.id);
81 jsonw_string_field(json_wtr, "attach_type",
82 attach_type_str);
83 jsonw_string_field(json_wtr, "attach_flags",
84 attach_flags_str);
85 jsonw_string_field(json_wtr, "name", info.name);
86 jsonw_end_object(json_wtr);
87 } else {
Roman Gushchin2058b382018-07-06 14:28:14 -070088 printf("%s%-8u %-15s %-15s %-15s\n", level ? " " : "",
89 info.id,
Roman Gushchin5ccda642017-12-13 15:18:54 +000090 attach_type_str,
91 attach_flags_str,
92 info.name);
93 }
94
95 close(prog_fd);
96 return 0;
97}
98
Roman Gushchin2058b382018-07-06 14:28:14 -070099static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
100{
101 __u32 prog_cnt = 0;
102 int ret;
103
104 ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt);
105 if (ret)
106 return -1;
107
108 return prog_cnt;
109}
110
111static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
112 int level)
Roman Gushchin5ccda642017-12-13 15:18:54 +0000113{
114 __u32 prog_ids[1024] = {0};
115 char *attach_flags_str;
116 __u32 prog_cnt, iter;
117 __u32 attach_flags;
118 char buf[32];
119 int ret;
120
121 prog_cnt = ARRAY_SIZE(prog_ids);
122 ret = bpf_prog_query(cgroup_fd, type, 0, &attach_flags, prog_ids,
123 &prog_cnt);
124 if (ret)
125 return ret;
126
127 if (prog_cnt == 0)
128 return 0;
129
130 switch (attach_flags) {
131 case BPF_F_ALLOW_MULTI:
132 attach_flags_str = "multi";
133 break;
134 case BPF_F_ALLOW_OVERRIDE:
135 attach_flags_str = "override";
136 break;
137 case 0:
138 attach_flags_str = "";
139 break;
140 default:
141 snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
142 attach_flags_str = buf;
143 }
144
145 for (iter = 0; iter < prog_cnt; iter++)
Jakub Kicinski65b875b2018-01-02 14:48:35 -0800146 show_bpf_prog(prog_ids[iter], attach_type_strings[type],
Roman Gushchin2058b382018-07-06 14:28:14 -0700147 attach_flags_str, level);
Roman Gushchin5ccda642017-12-13 15:18:54 +0000148
149 return 0;
150}
151
Jakub Kicinski65b875b2018-01-02 14:48:35 -0800152static int do_show(int argc, char **argv)
Roman Gushchin5ccda642017-12-13 15:18:54 +0000153{
154 enum bpf_attach_type type;
155 int cgroup_fd;
156 int ret = -1;
157
158 if (argc < 1) {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800159 p_err("too few parameters for cgroup show");
Roman Gushchin5ccda642017-12-13 15:18:54 +0000160 goto exit;
161 } else if (argc > 1) {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800162 p_err("too many parameters for cgroup show");
Roman Gushchin5ccda642017-12-13 15:18:54 +0000163 goto exit;
164 }
165
166 cgroup_fd = open(argv[0], O_RDONLY);
167 if (cgroup_fd < 0) {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800168 p_err("can't open cgroup %s", argv[1]);
Roman Gushchin5ccda642017-12-13 15:18:54 +0000169 goto exit;
170 }
171
172 if (json_output)
173 jsonw_start_array(json_wtr);
174 else
175 printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
176 "AttachFlags", "Name");
177
178 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
179 /*
180 * Not all attach types may be supported, so it's expected,
181 * that some requests will fail.
Jakub Kicinski65b875b2018-01-02 14:48:35 -0800182 * If we were able to get the show for at least one
Roman Gushchin5ccda642017-12-13 15:18:54 +0000183 * attach type, let's return 0.
184 */
Roman Gushchin2058b382018-07-06 14:28:14 -0700185 if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
Roman Gushchin5ccda642017-12-13 15:18:54 +0000186 ret = 0;
187 }
188
189 if (json_output)
190 jsonw_end_array(json_wtr);
191
192 close(cgroup_fd);
193exit:
194 return ret;
195}
196
Roman Gushchin2058b382018-07-06 14:28:14 -0700197/*
198 * To distinguish nftw() errors and do_show_tree_fn() errors
199 * and avoid duplicating error messages, let's return -2
200 * from do_show_tree_fn() in case of error.
201 */
202#define NFTW_ERR -1
203#define SHOW_TREE_FN_ERR -2
204static int do_show_tree_fn(const char *fpath, const struct stat *sb,
205 int typeflag, struct FTW *ftw)
206{
207 enum bpf_attach_type type;
208 bool skip = true;
209 int cgroup_fd;
210
211 if (typeflag != FTW_D)
212 return 0;
213
214 cgroup_fd = open(fpath, O_RDONLY);
215 if (cgroup_fd < 0) {
216 p_err("can't open cgroup %s: %s", fpath, strerror(errno));
217 return SHOW_TREE_FN_ERR;
218 }
219
220 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
221 int count = count_attached_bpf_progs(cgroup_fd, type);
222
223 if (count < 0 && errno != EINVAL) {
224 p_err("can't query bpf programs attached to %s: %s",
225 fpath, strerror(errno));
226 close(cgroup_fd);
227 return SHOW_TREE_FN_ERR;
228 }
229 if (count > 0) {
230 skip = false;
231 break;
232 }
233 }
234
235 if (skip) {
236 close(cgroup_fd);
237 return 0;
238 }
239
240 if (json_output) {
241 jsonw_start_object(json_wtr);
242 jsonw_string_field(json_wtr, "cgroup", fpath);
243 jsonw_name(json_wtr, "programs");
244 jsonw_start_array(json_wtr);
245 } else {
246 printf("%s\n", fpath);
247 }
248
249 for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
250 show_attached_bpf_progs(cgroup_fd, type, ftw->level);
251
Quentin Monnet39c9f102019-04-12 14:29:35 +0100252 if (errno == EINVAL)
253 /* Last attach type does not support query.
254 * Do not report an error for this, especially because batch
255 * mode would stop processing commands.
256 */
257 errno = 0;
258
Roman Gushchin2058b382018-07-06 14:28:14 -0700259 if (json_output) {
260 jsonw_end_array(json_wtr);
261 jsonw_end_object(json_wtr);
262 }
263
264 close(cgroup_fd);
265
266 return 0;
267}
268
269static char *find_cgroup_root(void)
270{
271 struct mntent *mnt;
272 FILE *f;
273
274 f = fopen("/proc/mounts", "r");
275 if (f == NULL)
276 return NULL;
277
278 while ((mnt = getmntent(f))) {
279 if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
280 fclose(f);
281 return strdup(mnt->mnt_dir);
282 }
283 }
284
285 fclose(f);
286 return NULL;
287}
288
289static int do_show_tree(int argc, char **argv)
290{
291 char *cgroup_root;
292 int ret;
293
294 switch (argc) {
295 case 0:
296 cgroup_root = find_cgroup_root();
297 if (!cgroup_root) {
298 p_err("cgroup v2 isn't mounted");
299 return -1;
300 }
301 break;
302 case 1:
303 cgroup_root = argv[0];
304 break;
305 default:
306 p_err("too many parameters for cgroup tree");
307 return -1;
308 }
309
310
311 if (json_output)
312 jsonw_start_array(json_wtr);
313 else
314 printf("%s\n"
315 "%-8s %-15s %-15s %-15s\n",
316 "CgroupPath",
317 "ID", "AttachType", "AttachFlags", "Name");
318
319 switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
320 case NFTW_ERR:
321 p_err("can't iterate over %s: %s", cgroup_root,
322 strerror(errno));
323 ret = -1;
324 break;
325 case SHOW_TREE_FN_ERR:
326 ret = -1;
327 break;
328 default:
329 ret = 0;
330 }
331
332 if (json_output)
333 jsonw_end_array(json_wtr);
334
335 if (argc == 0)
336 free(cgroup_root);
337
338 return ret;
339}
340
Roman Gushchin5ccda642017-12-13 15:18:54 +0000341static int do_attach(int argc, char **argv)
342{
343 enum bpf_attach_type attach_type;
344 int cgroup_fd, prog_fd;
345 int attach_flags = 0;
346 int ret = -1;
347 int i;
348
349 if (argc < 4) {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800350 p_err("too few parameters for cgroup attach");
Roman Gushchin5ccda642017-12-13 15:18:54 +0000351 goto exit;
352 }
353
354 cgroup_fd = open(argv[0], O_RDONLY);
355 if (cgroup_fd < 0) {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800356 p_err("can't open cgroup %s", argv[1]);
Roman Gushchin5ccda642017-12-13 15:18:54 +0000357 goto exit;
358 }
359
360 attach_type = parse_attach_type(argv[1]);
361 if (attach_type == __MAX_BPF_ATTACH_TYPE) {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800362 p_err("invalid attach type");
Roman Gushchin5ccda642017-12-13 15:18:54 +0000363 goto exit_cgroup;
364 }
365
366 argc -= 2;
367 argv = &argv[2];
368 prog_fd = prog_parse_fd(&argc, &argv);
369 if (prog_fd < 0)
370 goto exit_cgroup;
371
372 for (i = 0; i < argc; i++) {
373 if (is_prefix(argv[i], "multi")) {
374 attach_flags |= BPF_F_ALLOW_MULTI;
375 } else if (is_prefix(argv[i], "override")) {
376 attach_flags |= BPF_F_ALLOW_OVERRIDE;
377 } else {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800378 p_err("unknown option: %s", argv[i]);
Roman Gushchin5ccda642017-12-13 15:18:54 +0000379 goto exit_cgroup;
380 }
381 }
382
383 if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
384 p_err("failed to attach program");
385 goto exit_prog;
386 }
387
388 if (json_output)
389 jsonw_null(json_wtr);
390
391 ret = 0;
392
393exit_prog:
394 close(prog_fd);
395exit_cgroup:
396 close(cgroup_fd);
397exit:
398 return ret;
399}
400
401static int do_detach(int argc, char **argv)
402{
403 enum bpf_attach_type attach_type;
404 int prog_fd, cgroup_fd;
405 int ret = -1;
406
407 if (argc < 4) {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800408 p_err("too few parameters for cgroup detach");
Roman Gushchin5ccda642017-12-13 15:18:54 +0000409 goto exit;
410 }
411
412 cgroup_fd = open(argv[0], O_RDONLY);
413 if (cgroup_fd < 0) {
Jakub Kicinskib4fac962018-01-02 14:48:37 -0800414 p_err("can't open cgroup %s", argv[1]);
Roman Gushchin5ccda642017-12-13 15:18:54 +0000415 goto exit;
416 }
417
418 attach_type = parse_attach_type(argv[1]);
419 if (attach_type == __MAX_BPF_ATTACH_TYPE) {
420 p_err("invalid attach type");
421 goto exit_cgroup;
422 }
423
424 argc -= 2;
425 argv = &argv[2];
426 prog_fd = prog_parse_fd(&argc, &argv);
427 if (prog_fd < 0)
428 goto exit_cgroup;
429
430 if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
431 p_err("failed to detach program");
432 goto exit_prog;
433 }
434
435 if (json_output)
436 jsonw_null(json_wtr);
437
438 ret = 0;
439
440exit_prog:
441 close(prog_fd);
442exit_cgroup:
443 close(cgroup_fd);
444exit:
445 return ret;
446}
447
448static int do_help(int argc, char **argv)
449{
450 if (json_output) {
451 jsonw_null(json_wtr);
452 return 0;
453 }
454
455 fprintf(stderr,
Jakub Kicinski6ebe6db2018-01-02 14:48:36 -0800456 "Usage: %s %s { show | list } CGROUP\n"
Roman Gushchin2058b382018-07-06 14:28:14 -0700457 " %s %s tree [CGROUP_ROOT]\n"
Roman Gushchin5ccda642017-12-13 15:18:54 +0000458 " %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
459 " %s %s detach CGROUP ATTACH_TYPE PROG\n"
460 " %s %s help\n"
461 "\n"
Andrey Ignatov393de512018-04-17 10:28:44 -0700462 HELP_SPEC_ATTACH_TYPES "\n"
Roman Gushchin5ccda642017-12-13 15:18:54 +0000463 " " HELP_SPEC_ATTACH_FLAGS "\n"
464 " " HELP_SPEC_PROGRAM "\n"
465 " " HELP_SPEC_OPTIONS "\n"
466 "",
Roman Gushchin2058b382018-07-06 14:28:14 -0700467 bin_name, argv[-2],
Roman Gushchin5ccda642017-12-13 15:18:54 +0000468 bin_name, argv[-2], bin_name, argv[-2],
469 bin_name, argv[-2], bin_name, argv[-2]);
470
471 return 0;
472}
473
474static const struct cmd cmds[] = {
Jakub Kicinski6ebe6db2018-01-02 14:48:36 -0800475 { "show", do_show },
Jakub Kicinski65b875b2018-01-02 14:48:35 -0800476 { "list", do_show },
Roman Gushchin2058b382018-07-06 14:28:14 -0700477 { "tree", do_show_tree },
Roman Gushchin5ccda642017-12-13 15:18:54 +0000478 { "attach", do_attach },
479 { "detach", do_detach },
480 { "help", do_help },
481 { 0 }
482};
483
484int do_cgroup(int argc, char **argv)
485{
486 return cmd_select(cmds, argc, argv, do_help);
487}