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