linux/tools/testing/selftests/bpf/test_cgroup_attach.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3/* eBPF example program:
   4 *
   5 * - Creates arraymap in kernel with 4 bytes keys and 8 byte values
   6 *
   7 * - Loads eBPF program
   8 *
   9 *   The eBPF program accesses the map passed in to store two pieces of
  10 *   information. The number of invocations of the program, which maps
  11 *   to the number of packets received, is stored to key 0. Key 1 is
  12 *   incremented on each iteration by the number of bytes stored in
  13 *   the skb. The program also stores the number of received bytes
  14 *   in the cgroup storage.
  15 *
  16 * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
  17 *
  18 * - Every second, reads map[0] and map[1] to see how many bytes and
  19 *   packets were seen on any socket of tasks in the given cgroup.
  20 */
  21
  22#define _GNU_SOURCE
  23
  24#include <stdio.h>
  25#include <stdlib.h>
  26#include <assert.h>
  27#include <sys/resource.h>
  28#include <sys/time.h>
  29#include <unistd.h>
  30#include <linux/filter.h>
  31
  32#include <linux/bpf.h>
  33#include <bpf/bpf.h>
  34
  35#include "bpf_util.h"
  36#include "bpf_rlimit.h"
  37#include "cgroup_helpers.h"
  38
  39#define FOO             "/foo"
  40#define BAR             "/foo/bar/"
  41#define PING_CMD        "ping -q -c1 -w1 127.0.0.1 > /dev/null"
  42
  43char bpf_log_buf[BPF_LOG_BUF_SIZE];
  44
  45#ifdef DEBUG
  46#define debug(args...) printf(args)
  47#else
  48#define debug(args...)
  49#endif
  50
  51static int prog_load(int verdict)
  52{
  53        int ret;
  54        struct bpf_insn prog[] = {
  55                BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
  56                BPF_EXIT_INSN(),
  57        };
  58        size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
  59
  60        ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
  61                               prog, insns_cnt, "GPL", 0,
  62                               bpf_log_buf, BPF_LOG_BUF_SIZE);
  63
  64        if (ret < 0) {
  65                log_err("Loading program");
  66                printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
  67                return 0;
  68        }
  69        return ret;
  70}
  71
  72static int test_foo_bar(void)
  73{
  74        int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0;
  75
  76        allow_prog = prog_load(1);
  77        if (!allow_prog)
  78                goto err;
  79
  80        drop_prog = prog_load(0);
  81        if (!drop_prog)
  82                goto err;
  83
  84        if (setup_cgroup_environment())
  85                goto err;
  86
  87        /* Create cgroup /foo, get fd, and join it */
  88        foo = create_and_get_cgroup(FOO);
  89        if (foo < 0)
  90                goto err;
  91
  92        if (join_cgroup(FOO))
  93                goto err;
  94
  95        if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS,
  96                            BPF_F_ALLOW_OVERRIDE)) {
  97                log_err("Attaching prog to /foo");
  98                goto err;
  99        }
 100
 101        debug("Attached DROP prog. This ping in cgroup /foo should fail...\n");
 102        assert(system(PING_CMD) != 0);
 103
 104        /* Create cgroup /foo/bar, get fd, and join it */
 105        bar = create_and_get_cgroup(BAR);
 106        if (bar < 0)
 107                goto err;
 108
 109        if (join_cgroup(BAR))
 110                goto err;
 111
 112        debug("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
 113        assert(system(PING_CMD) != 0);
 114
 115        if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
 116                            BPF_F_ALLOW_OVERRIDE)) {
 117                log_err("Attaching prog to /foo/bar");
 118                goto err;
 119        }
 120
 121        debug("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
 122        assert(system(PING_CMD) == 0);
 123
 124        if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
 125                log_err("Detaching program from /foo/bar");
 126                goto err;
 127        }
 128
 129        debug("Detached PASS from /foo/bar while DROP is attached to /foo.\n"
 130               "This ping in cgroup /foo/bar should fail...\n");
 131        assert(system(PING_CMD) != 0);
 132
 133        if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
 134                            BPF_F_ALLOW_OVERRIDE)) {
 135                log_err("Attaching prog to /foo/bar");
 136                goto err;
 137        }
 138
 139        if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
 140                log_err("Detaching program from /foo");
 141                goto err;
 142        }
 143
 144        debug("Attached PASS from /foo/bar and detached DROP from /foo.\n"
 145               "This ping in cgroup /foo/bar should pass...\n");
 146        assert(system(PING_CMD) == 0);
 147
 148        if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
 149                            BPF_F_ALLOW_OVERRIDE)) {
 150                log_err("Attaching prog to /foo/bar");
 151                goto err;
 152        }
 153
 154        if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
 155                errno = 0;
 156                log_err("Unexpected success attaching prog to /foo/bar");
 157                goto err;
 158        }
 159
 160        if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
 161                log_err("Detaching program from /foo/bar");
 162                goto err;
 163        }
 164
 165        if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
 166                errno = 0;
 167                log_err("Unexpected success in double detach from /foo");
 168                goto err;
 169        }
 170
 171        if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
 172                log_err("Attaching non-overridable prog to /foo");
 173                goto err;
 174        }
 175
 176        if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
 177                errno = 0;
 178                log_err("Unexpected success attaching non-overridable prog to /foo/bar");
 179                goto err;
 180        }
 181
 182        if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
 183                             BPF_F_ALLOW_OVERRIDE)) {
 184                errno = 0;
 185                log_err("Unexpected success attaching overridable prog to /foo/bar");
 186                goto err;
 187        }
 188
 189        if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS,
 190                             BPF_F_ALLOW_OVERRIDE)) {
 191                errno = 0;
 192                log_err("Unexpected success attaching overridable prog to /foo");
 193                goto err;
 194        }
 195
 196        if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
 197                log_err("Attaching different non-overridable prog to /foo");
 198                goto err;
 199        }
 200
 201        goto out;
 202
 203err:
 204        rc = 1;
 205
 206out:
 207        close(foo);
 208        close(bar);
 209        cleanup_cgroup_environment();
 210        if (!rc)
 211                printf("#override:PASS\n");
 212        else
 213                printf("#override:FAIL\n");
 214        return rc;
 215}
 216
 217static int map_fd = -1;
 218
 219static int prog_load_cnt(int verdict, int val)
 220{
 221        int cgroup_storage_fd, percpu_cgroup_storage_fd;
 222
 223        if (map_fd < 0)
 224                map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0);
 225        if (map_fd < 0) {
 226                printf("failed to create map '%s'\n", strerror(errno));
 227                return -1;
 228        }
 229
 230        cgroup_storage_fd = bpf_create_map(BPF_MAP_TYPE_CGROUP_STORAGE,
 231                                sizeof(struct bpf_cgroup_storage_key), 8, 0, 0);
 232        if (cgroup_storage_fd < 0) {
 233                printf("failed to create map '%s'\n", strerror(errno));
 234                return -1;
 235        }
 236
 237        percpu_cgroup_storage_fd = bpf_create_map(
 238                BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
 239                sizeof(struct bpf_cgroup_storage_key), 8, 0, 0);
 240        if (percpu_cgroup_storage_fd < 0) {
 241                printf("failed to create map '%s'\n", strerror(errno));
 242                return -1;
 243        }
 244
 245        struct bpf_insn prog[] = {
 246                BPF_MOV32_IMM(BPF_REG_0, 0),
 247                BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
 248                BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
 249                BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
 250                BPF_LD_MAP_FD(BPF_REG_1, map_fd),
 251                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
 252                BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
 253                BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */
 254                BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
 255
 256                BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd),
 257                BPF_MOV64_IMM(BPF_REG_2, 0),
 258                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
 259                BPF_MOV64_IMM(BPF_REG_1, val),
 260                BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0),
 261
 262                BPF_LD_MAP_FD(BPF_REG_1, percpu_cgroup_storage_fd),
 263                BPF_MOV64_IMM(BPF_REG_2, 0),
 264                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
 265                BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0),
 266                BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 0x1),
 267                BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_3, 0),
 268
 269                BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
 270                BPF_EXIT_INSN(),
 271        };
 272        size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
 273        int ret;
 274
 275        ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
 276                               prog, insns_cnt, "GPL", 0,
 277                               bpf_log_buf, BPF_LOG_BUF_SIZE);
 278
 279        if (ret < 0) {
 280                log_err("Loading program");
 281                printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
 282                return 0;
 283        }
 284        close(cgroup_storage_fd);
 285        return ret;
 286}
 287
 288
 289static int test_multiprog(void)
 290{
 291        __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id;
 292        int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0;
 293        int drop_prog, allow_prog[6] = {}, rc = 0;
 294        unsigned long long value;
 295        int i = 0;
 296
 297        for (i = 0; i < 6; i++) {
 298                allow_prog[i] = prog_load_cnt(1, 1 << i);
 299                if (!allow_prog[i])
 300                        goto err;
 301        }
 302        drop_prog = prog_load_cnt(0, 1);
 303        if (!drop_prog)
 304                goto err;
 305
 306        if (setup_cgroup_environment())
 307                goto err;
 308
 309        cg1 = create_and_get_cgroup("/cg1");
 310        if (cg1 < 0)
 311                goto err;
 312        cg2 = create_and_get_cgroup("/cg1/cg2");
 313        if (cg2 < 0)
 314                goto err;
 315        cg3 = create_and_get_cgroup("/cg1/cg2/cg3");
 316        if (cg3 < 0)
 317                goto err;
 318        cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4");
 319        if (cg4 < 0)
 320                goto err;
 321        cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5");
 322        if (cg5 < 0)
 323                goto err;
 324
 325        if (join_cgroup("/cg1/cg2/cg3/cg4/cg5"))
 326                goto err;
 327
 328        if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
 329                            BPF_F_ALLOW_MULTI)) {
 330                log_err("Attaching prog to cg1");
 331                goto err;
 332        }
 333        if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
 334                             BPF_F_ALLOW_MULTI)) {
 335                log_err("Unexpected success attaching the same prog to cg1");
 336                goto err;
 337        }
 338        if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS,
 339                            BPF_F_ALLOW_MULTI)) {
 340                log_err("Attaching prog2 to cg1");
 341                goto err;
 342        }
 343        if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS,
 344                            BPF_F_ALLOW_OVERRIDE)) {
 345                log_err("Attaching prog to cg2");
 346                goto err;
 347        }
 348        if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS,
 349                            BPF_F_ALLOW_MULTI)) {
 350                log_err("Attaching prog to cg3");
 351                goto err;
 352        }
 353        if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS,
 354                            BPF_F_ALLOW_OVERRIDE)) {
 355                log_err("Attaching prog to cg4");
 356                goto err;
 357        }
 358        if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) {
 359                log_err("Attaching prog to cg5");
 360                goto err;
 361        }
 362        assert(system(PING_CMD) == 0);
 363        assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 364        assert(value == 1 + 2 + 8 + 32);
 365
 366        /* query the number of effective progs in cg5 */
 367        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
 368                              NULL, NULL, &prog_cnt) == 0);
 369        assert(prog_cnt == 4);
 370        /* retrieve prog_ids of effective progs in cg5 */
 371        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
 372                              &attach_flags, prog_ids, &prog_cnt) == 0);
 373        assert(prog_cnt == 4);
 374        assert(attach_flags == 0);
 375        saved_prog_id = prog_ids[0];
 376        /* check enospc handling */
 377        prog_ids[0] = 0;
 378        prog_cnt = 2;
 379        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
 380                              &attach_flags, prog_ids, &prog_cnt) == -1 &&
 381               errno == ENOSPC);
 382        assert(prog_cnt == 4);
 383        /* check that prog_ids are returned even when buffer is too small */
 384        assert(prog_ids[0] == saved_prog_id);
 385        /* retrieve prog_id of single attached prog in cg5 */
 386        prog_ids[0] = 0;
 387        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
 388                              NULL, prog_ids, &prog_cnt) == 0);
 389        assert(prog_cnt == 1);
 390        assert(prog_ids[0] == saved_prog_id);
 391
 392        /* detach bottom program and ping again */
 393        if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) {
 394                log_err("Detaching prog from cg5");
 395                goto err;
 396        }
 397        value = 0;
 398        assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
 399        assert(system(PING_CMD) == 0);
 400        assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 401        assert(value == 1 + 2 + 8 + 16);
 402
 403        /* detach 3rd from bottom program and ping again */
 404        errno = 0;
 405        if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) {
 406                log_err("Unexpected success on detach from cg3");
 407                goto err;
 408        }
 409        if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) {
 410                log_err("Detaching from cg3");
 411                goto err;
 412        }
 413        value = 0;
 414        assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
 415        assert(system(PING_CMD) == 0);
 416        assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 417        assert(value == 1 + 2 + 16);
 418
 419        /* detach 2nd from bottom program and ping again */
 420        if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) {
 421                log_err("Detaching prog from cg4");
 422                goto err;
 423        }
 424        value = 0;
 425        assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
 426        assert(system(PING_CMD) == 0);
 427        assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 428        assert(value == 1 + 2 + 4);
 429
 430        prog_cnt = 4;
 431        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
 432                              &attach_flags, prog_ids, &prog_cnt) == 0);
 433        assert(prog_cnt == 3);
 434        assert(attach_flags == 0);
 435        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
 436                              NULL, prog_ids, &prog_cnt) == 0);
 437        assert(prog_cnt == 0);
 438        goto out;
 439err:
 440        rc = 1;
 441
 442out:
 443        for (i = 0; i < 6; i++)
 444                if (allow_prog[i] > 0)
 445                        close(allow_prog[i]);
 446        close(cg1);
 447        close(cg2);
 448        close(cg3);
 449        close(cg4);
 450        close(cg5);
 451        cleanup_cgroup_environment();
 452        if (!rc)
 453                printf("#multi:PASS\n");
 454        else
 455                printf("#multi:FAIL\n");
 456        return rc;
 457}
 458
 459static int test_autodetach(void)
 460{
 461        __u32 prog_cnt = 4, attach_flags;
 462        int allow_prog[2] = {0};
 463        __u32 prog_ids[2] = {0};
 464        int cg = 0, i, rc = -1;
 465        void *ptr = NULL;
 466        int attempts;
 467
 468        for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
 469                allow_prog[i] = prog_load_cnt(1, 1 << i);
 470                if (!allow_prog[i])
 471                        goto err;
 472        }
 473
 474        if (setup_cgroup_environment())
 475                goto err;
 476
 477        /* create a cgroup, attach two programs and remember their ids */
 478        cg = create_and_get_cgroup("/cg_autodetach");
 479        if (cg < 0)
 480                goto err;
 481
 482        if (join_cgroup("/cg_autodetach"))
 483                goto err;
 484
 485        for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
 486                if (bpf_prog_attach(allow_prog[i], cg, BPF_CGROUP_INET_EGRESS,
 487                                    BPF_F_ALLOW_MULTI)) {
 488                        log_err("Attaching prog[%d] to cg:egress", i);
 489                        goto err;
 490                }
 491        }
 492
 493        /* make sure that programs are attached and run some traffic */
 494        assert(bpf_prog_query(cg, BPF_CGROUP_INET_EGRESS, 0, &attach_flags,
 495                              prog_ids, &prog_cnt) == 0);
 496        assert(system(PING_CMD) == 0);
 497
 498        /* allocate some memory (4Mb) to pin the original cgroup */
 499        ptr = malloc(4 * (1 << 20));
 500        if (!ptr)
 501                goto err;
 502
 503        /* close programs and cgroup fd */
 504        for (i = 0; i < ARRAY_SIZE(allow_prog); i++) {
 505                close(allow_prog[i]);
 506                allow_prog[i] = 0;
 507        }
 508
 509        close(cg);
 510        cg = 0;
 511
 512        /* leave the cgroup and remove it. don't detach programs */
 513        cleanup_cgroup_environment();
 514
 515        /* wait for the asynchronous auto-detachment.
 516         * wait for no more than 5 sec and give up.
 517         */
 518        for (i = 0; i < ARRAY_SIZE(prog_ids); i++) {
 519                for (attempts = 5; attempts >= 0; attempts--) {
 520                        int fd = bpf_prog_get_fd_by_id(prog_ids[i]);
 521
 522                        if (fd < 0)
 523                                break;
 524
 525                        /* don't leave the fd open */
 526                        close(fd);
 527
 528                        if (!attempts)
 529                                goto err;
 530
 531                        sleep(1);
 532                }
 533        }
 534
 535        rc = 0;
 536err:
 537        for (i = 0; i < ARRAY_SIZE(allow_prog); i++)
 538                if (allow_prog[i] > 0)
 539                        close(allow_prog[i]);
 540        if (cg)
 541                close(cg);
 542        free(ptr);
 543        cleanup_cgroup_environment();
 544        if (!rc)
 545                printf("#autodetach:PASS\n");
 546        else
 547                printf("#autodetach:FAIL\n");
 548        return rc;
 549}
 550
 551int main(void)
 552{
 553        int (*tests[])(void) = {
 554                test_foo_bar,
 555                test_multiprog,
 556                test_autodetach,
 557        };
 558        int errors = 0;
 559        int i;
 560
 561        for (i = 0; i < ARRAY_SIZE(tests); i++)
 562                if (tests[i]())
 563                        errors++;
 564
 565        if (errors)
 566                printf("test_cgroup_attach:FAIL\n");
 567        else
 568                printf("test_cgroup_attach:PASS\n");
 569
 570        return errors ? EXIT_FAILURE : EXIT_SUCCESS;
 571}
 572