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