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