linux/samples/bpf/test_cgrp2_attach2.c
<<
>>
Prefs
   1/* eBPF example program:
   2 *
   3 * - Creates arraymap in kernel with 4 bytes keys and 8 byte values
   4 *
   5 * - Loads eBPF program
   6 *
   7 *   The eBPF program accesses the map passed in to store two pieces of
   8 *   information. The number of invocations of the program, which maps
   9 *   to the number of packets received, is stored to key 0. Key 1 is
  10 *   incremented on each iteration by the number of bytes stored in
  11 *   the skb.
  12 *
  13 * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
  14 *
  15 * - Every second, reads map[0] and map[1] to see how many bytes and
  16 *   packets were seen on any socket of tasks in the given cgroup.
  17 */
  18
  19#define _GNU_SOURCE
  20
  21#include <stdio.h>
  22#include <stdlib.h>
  23#include <assert.h>
  24#include <unistd.h>
  25
  26#include <linux/bpf.h>
  27
  28#include "libbpf.h"
  29#include "cgroup_helpers.h"
  30
  31#define FOO             "/foo"
  32#define BAR             "/foo/bar/"
  33#define PING_CMD        "ping -c1 -w1 127.0.0.1 > /dev/null"
  34
  35char bpf_log_buf[BPF_LOG_BUF_SIZE];
  36
  37static int prog_load(int verdict)
  38{
  39        int ret;
  40        struct bpf_insn prog[] = {
  41                BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
  42                BPF_EXIT_INSN(),
  43        };
  44        size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
  45
  46        ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
  47                               prog, insns_cnt, "GPL", 0,
  48                               bpf_log_buf, BPF_LOG_BUF_SIZE);
  49
  50        if (ret < 0) {
  51                log_err("Loading program");
  52                printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
  53                return 0;
  54        }
  55        return ret;
  56}
  57
  58static int test_foo_bar(void)
  59{
  60        int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0;
  61
  62        allow_prog = prog_load(1);
  63        if (!allow_prog)
  64                goto err;
  65
  66        drop_prog = prog_load(0);
  67        if (!drop_prog)
  68                goto err;
  69
  70        if (setup_cgroup_environment())
  71                goto err;
  72
  73        /* Create cgroup /foo, get fd, and join it */
  74        foo = create_and_get_cgroup(FOO);
  75        if (!foo)
  76                goto err;
  77
  78        if (join_cgroup(FOO))
  79                goto err;
  80
  81        if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS,
  82                            BPF_F_ALLOW_OVERRIDE)) {
  83                log_err("Attaching prog to /foo");
  84                goto err;
  85        }
  86
  87        printf("Attached DROP prog. This ping in cgroup /foo should fail...\n");
  88        assert(system(PING_CMD) != 0);
  89
  90        /* Create cgroup /foo/bar, get fd, and join it */
  91        bar = create_and_get_cgroup(BAR);
  92        if (!bar)
  93                goto err;
  94
  95        if (join_cgroup(BAR))
  96                goto err;
  97
  98        printf("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n");
  99        assert(system(PING_CMD) != 0);
 100
 101        if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
 102                            BPF_F_ALLOW_OVERRIDE)) {
 103                log_err("Attaching prog to /foo/bar");
 104                goto err;
 105        }
 106
 107        printf("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n");
 108        assert(system(PING_CMD) == 0);
 109
 110        if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
 111                log_err("Detaching program from /foo/bar");
 112                goto err;
 113        }
 114
 115        printf("Detached PASS from /foo/bar while DROP is attached to /foo.\n"
 116               "This ping in cgroup /foo/bar should fail...\n");
 117        assert(system(PING_CMD) != 0);
 118
 119        if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
 120                            BPF_F_ALLOW_OVERRIDE)) {
 121                log_err("Attaching prog to /foo/bar");
 122                goto err;
 123        }
 124
 125        if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
 126                log_err("Detaching program from /foo");
 127                goto err;
 128        }
 129
 130        printf("Attached PASS from /foo/bar and detached DROP from /foo.\n"
 131               "This ping in cgroup /foo/bar should pass...\n");
 132        assert(system(PING_CMD) == 0);
 133
 134        if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
 135                            BPF_F_ALLOW_OVERRIDE)) {
 136                log_err("Attaching prog to /foo/bar");
 137                goto err;
 138        }
 139
 140        if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
 141                errno = 0;
 142                log_err("Unexpected success attaching prog to /foo/bar");
 143                goto err;
 144        }
 145
 146        if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) {
 147                log_err("Detaching program from /foo/bar");
 148                goto err;
 149        }
 150
 151        if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) {
 152                errno = 0;
 153                log_err("Unexpected success in double detach from /foo");
 154                goto err;
 155        }
 156
 157        if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
 158                log_err("Attaching non-overridable prog to /foo");
 159                goto err;
 160        }
 161
 162        if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) {
 163                errno = 0;
 164                log_err("Unexpected success attaching non-overridable prog to /foo/bar");
 165                goto err;
 166        }
 167
 168        if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS,
 169                             BPF_F_ALLOW_OVERRIDE)) {
 170                errno = 0;
 171                log_err("Unexpected success attaching overridable prog to /foo/bar");
 172                goto err;
 173        }
 174
 175        if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS,
 176                             BPF_F_ALLOW_OVERRIDE)) {
 177                errno = 0;
 178                log_err("Unexpected success attaching overridable prog to /foo");
 179                goto err;
 180        }
 181
 182        if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) {
 183                log_err("Attaching different non-overridable prog to /foo");
 184                goto err;
 185        }
 186
 187        goto out;
 188
 189err:
 190        rc = 1;
 191
 192out:
 193        close(foo);
 194        close(bar);
 195        cleanup_cgroup_environment();
 196        if (!rc)
 197                printf("### override:PASS\n");
 198        else
 199                printf("### override:FAIL\n");
 200        return rc;
 201}
 202
 203static int map_fd = -1;
 204
 205static int prog_load_cnt(int verdict, int val)
 206{
 207        if (map_fd < 0)
 208                map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0);
 209        if (map_fd < 0) {
 210                printf("failed to create map '%s'\n", strerror(errno));
 211                return -1;
 212        }
 213
 214        struct bpf_insn prog[] = {
 215                BPF_MOV32_IMM(BPF_REG_0, 0),
 216                BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
 217                BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
 218                BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
 219                BPF_LD_MAP_FD(BPF_REG_1, map_fd),
 220                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
 221                BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
 222                BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */
 223                BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
 224                BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
 225                BPF_EXIT_INSN(),
 226        };
 227        size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
 228        int ret;
 229
 230        ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
 231                               prog, insns_cnt, "GPL", 0,
 232                               bpf_log_buf, BPF_LOG_BUF_SIZE);
 233
 234        if (ret < 0) {
 235                log_err("Loading program");
 236                printf("Output from verifier:\n%s\n-------\n", bpf_log_buf);
 237                return 0;
 238        }
 239        return ret;
 240}
 241
 242
 243static int test_multiprog(void)
 244{
 245        __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id;
 246        int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0;
 247        int drop_prog, allow_prog[6] = {}, rc = 0;
 248        unsigned long long value;
 249        int i = 0;
 250
 251        for (i = 0; i < 6; i++) {
 252                allow_prog[i] = prog_load_cnt(1, 1 << i);
 253                if (!allow_prog[i])
 254                        goto err;
 255        }
 256        drop_prog = prog_load_cnt(0, 1);
 257        if (!drop_prog)
 258                goto err;
 259
 260        if (setup_cgroup_environment())
 261                goto err;
 262
 263        cg1 = create_and_get_cgroup("/cg1");
 264        if (!cg1)
 265                goto err;
 266        cg2 = create_and_get_cgroup("/cg1/cg2");
 267        if (!cg2)
 268                goto err;
 269        cg3 = create_and_get_cgroup("/cg1/cg2/cg3");
 270        if (!cg3)
 271                goto err;
 272        cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4");
 273        if (!cg4)
 274                goto err;
 275        cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5");
 276        if (!cg5)
 277                goto err;
 278
 279        if (join_cgroup("/cg1/cg2/cg3/cg4/cg5"))
 280                goto err;
 281
 282        if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
 283                            BPF_F_ALLOW_MULTI)) {
 284                log_err("Attaching prog to cg1");
 285                goto err;
 286        }
 287        if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS,
 288                             BPF_F_ALLOW_MULTI)) {
 289                log_err("Unexpected success attaching the same prog to cg1");
 290                goto err;
 291        }
 292        if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS,
 293                            BPF_F_ALLOW_MULTI)) {
 294                log_err("Attaching prog2 to cg1");
 295                goto err;
 296        }
 297        if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS,
 298                            BPF_F_ALLOW_OVERRIDE)) {
 299                log_err("Attaching prog to cg2");
 300                goto err;
 301        }
 302        if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS,
 303                            BPF_F_ALLOW_MULTI)) {
 304                log_err("Attaching prog to cg3");
 305                goto err;
 306        }
 307        if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS,
 308                            BPF_F_ALLOW_OVERRIDE)) {
 309                log_err("Attaching prog to cg4");
 310                goto err;
 311        }
 312        if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) {
 313                log_err("Attaching prog to cg5");
 314                goto err;
 315        }
 316        assert(system(PING_CMD) == 0);
 317        assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 318        assert(value == 1 + 2 + 8 + 32);
 319
 320        /* query the number of effective progs in cg5 */
 321        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
 322                              NULL, NULL, &prog_cnt) == 0);
 323        assert(prog_cnt == 4);
 324        /* retrieve prog_ids of effective progs in cg5 */
 325        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
 326                              &attach_flags, prog_ids, &prog_cnt) == 0);
 327        assert(prog_cnt == 4);
 328        assert(attach_flags == 0);
 329        saved_prog_id = prog_ids[0];
 330        /* check enospc handling */
 331        prog_ids[0] = 0;
 332        prog_cnt = 2;
 333        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
 334                              &attach_flags, prog_ids, &prog_cnt) == -1 &&
 335               errno == ENOSPC);
 336        assert(prog_cnt == 4);
 337        /* check that prog_ids are returned even when buffer is too small */
 338        assert(prog_ids[0] == saved_prog_id);
 339        /* retrieve prog_id of single attached prog in cg5 */
 340        prog_ids[0] = 0;
 341        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
 342                              NULL, prog_ids, &prog_cnt) == 0);
 343        assert(prog_cnt == 1);
 344        assert(prog_ids[0] == saved_prog_id);
 345
 346        /* detach bottom program and ping again */
 347        if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) {
 348                log_err("Detaching prog from cg5");
 349                goto err;
 350        }
 351        value = 0;
 352        assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
 353        assert(system(PING_CMD) == 0);
 354        assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 355        assert(value == 1 + 2 + 8 + 16);
 356
 357        /* detach 3rd from bottom program and ping again */
 358        errno = 0;
 359        if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) {
 360                log_err("Unexpected success on detach from cg3");
 361                goto err;
 362        }
 363        if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) {
 364                log_err("Detaching from cg3");
 365                goto err;
 366        }
 367        value = 0;
 368        assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
 369        assert(system(PING_CMD) == 0);
 370        assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 371        assert(value == 1 + 2 + 16);
 372
 373        /* detach 2nd from bottom program and ping again */
 374        if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) {
 375                log_err("Detaching prog from cg4");
 376                goto err;
 377        }
 378        value = 0;
 379        assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0);
 380        assert(system(PING_CMD) == 0);
 381        assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0);
 382        assert(value == 1 + 2 + 4);
 383
 384        prog_cnt = 4;
 385        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE,
 386                              &attach_flags, prog_ids, &prog_cnt) == 0);
 387        assert(prog_cnt == 3);
 388        assert(attach_flags == 0);
 389        assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0,
 390                              NULL, prog_ids, &prog_cnt) == 0);
 391        assert(prog_cnt == 0);
 392        goto out;
 393err:
 394        rc = 1;
 395
 396out:
 397        for (i = 0; i < 6; i++)
 398                if (allow_prog[i] > 0)
 399                        close(allow_prog[i]);
 400        close(cg1);
 401        close(cg2);
 402        close(cg3);
 403        close(cg4);
 404        close(cg5);
 405        cleanup_cgroup_environment();
 406        if (!rc)
 407                printf("### multi:PASS\n");
 408        else
 409                printf("### multi:FAIL\n");
 410        return rc;
 411}
 412
 413int main(int argc, char **argv)
 414{
 415        int rc = 0;
 416
 417        rc = test_foo_bar();
 418        if (rc)
 419                return rc;
 420
 421        return test_multiprog();
 422}
 423