linux/tools/testing/selftests/bpf/prog_tests/cgroup_link.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3#include <test_progs.h>
   4#include "cgroup_helpers.h"
   5#include "testing_helpers.h"
   6#include "test_cgroup_link.skel.h"
   7
   8static __u32 duration = 0;
   9#define PING_CMD        "ping -q -c1 -w1 127.0.0.1 > /dev/null"
  10
  11static struct test_cgroup_link *skel = NULL;
  12
  13int ping_and_check(int exp_calls, int exp_alt_calls)
  14{
  15        skel->bss->calls = 0;
  16        skel->bss->alt_calls = 0;
  17        CHECK_FAIL(system(PING_CMD));
  18        if (CHECK(skel->bss->calls != exp_calls, "call_cnt",
  19                  "exp %d, got %d\n", exp_calls, skel->bss->calls))
  20                return -EINVAL;
  21        if (CHECK(skel->bss->alt_calls != exp_alt_calls, "alt_call_cnt",
  22                  "exp %d, got %d\n", exp_alt_calls, skel->bss->alt_calls))
  23                return -EINVAL;
  24        return 0;
  25}
  26
  27void test_cgroup_link(void)
  28{
  29        struct {
  30                const char *path;
  31                int fd;
  32        } cgs[] = {
  33                { "/cg1" },
  34                { "/cg1/cg2" },
  35                { "/cg1/cg2/cg3" },
  36                { "/cg1/cg2/cg3/cg4" },
  37        };
  38        int last_cg = ARRAY_SIZE(cgs) - 1, cg_nr = ARRAY_SIZE(cgs);
  39        DECLARE_LIBBPF_OPTS(bpf_link_update_opts, link_upd_opts);
  40        struct bpf_link *links[ARRAY_SIZE(cgs)] = {}, *tmp_link;
  41        __u32 prog_ids[ARRAY_SIZE(cgs)], prog_cnt = 0, attach_flags, prog_id;
  42        struct bpf_link_info info;
  43        int i = 0, err, prog_fd;
  44        bool detach_legacy = false;
  45
  46        skel = test_cgroup_link__open_and_load();
  47        if (CHECK(!skel, "skel_open_load", "failed to open/load skeleton\n"))
  48                return;
  49        prog_fd = bpf_program__fd(skel->progs.egress);
  50
  51        err = setup_cgroup_environment();
  52        if (CHECK(err, "cg_init", "failed: %d\n", err))
  53                goto cleanup;
  54
  55        for (i = 0; i < cg_nr; i++) {
  56                cgs[i].fd = create_and_get_cgroup(cgs[i].path);
  57                if (CHECK(cgs[i].fd < 0, "cg_create", "fail: %d\n", cgs[i].fd))
  58                        goto cleanup;
  59        }
  60
  61        err = join_cgroup(cgs[last_cg].path);
  62        if (CHECK(err, "cg_join", "fail: %d\n", err))
  63                goto cleanup;
  64
  65        for (i = 0; i < cg_nr; i++) {
  66                links[i] = bpf_program__attach_cgroup(skel->progs.egress,
  67                                                      cgs[i].fd);
  68                if (CHECK(IS_ERR(links[i]), "cg_attach", "i: %d, err: %ld\n",
  69                                 i, PTR_ERR(links[i])))
  70                        goto cleanup;
  71        }
  72
  73        ping_and_check(cg_nr, 0);
  74
  75        /* query the number of effective progs and attach flags in root cg */
  76        err = bpf_prog_query(cgs[0].fd, BPF_CGROUP_INET_EGRESS,
  77                             BPF_F_QUERY_EFFECTIVE, &attach_flags, NULL,
  78                             &prog_cnt);
  79        CHECK_FAIL(err);
  80        CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
  81        if (CHECK(prog_cnt != 1, "effect_cnt", "exp %d, got %d\n", 1, prog_cnt))
  82                goto cleanup;
  83
  84        /* query the number of effective progs in last cg */
  85        err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS,
  86                             BPF_F_QUERY_EFFECTIVE, NULL, NULL,
  87                             &prog_cnt);
  88        CHECK_FAIL(err);
  89        CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
  90        if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n",
  91                  cg_nr, prog_cnt))
  92                goto cleanup;
  93
  94        /* query the effective prog IDs in last cg */
  95        err = bpf_prog_query(cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS,
  96                             BPF_F_QUERY_EFFECTIVE, &attach_flags,
  97                             prog_ids, &prog_cnt);
  98        CHECK_FAIL(err);
  99        CHECK_FAIL(attach_flags != BPF_F_ALLOW_MULTI);
 100        if (CHECK(prog_cnt != cg_nr, "effect_cnt", "exp %d, got %d\n",
 101                  cg_nr, prog_cnt))
 102                goto cleanup;
 103        for (i = 1; i < prog_cnt; i++) {
 104                CHECK(prog_ids[i - 1] != prog_ids[i], "prog_id_check",
 105                      "idx %d, prev id %d, cur id %d\n",
 106                      i, prog_ids[i - 1], prog_ids[i]);
 107        }
 108
 109        /* detach bottom program and ping again */
 110        bpf_link__destroy(links[last_cg]);
 111        links[last_cg] = NULL;
 112
 113        ping_and_check(cg_nr - 1, 0);
 114
 115        /* mix in with non link-based multi-attachments */
 116        err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
 117                              BPF_CGROUP_INET_EGRESS, BPF_F_ALLOW_MULTI);
 118        if (CHECK(err, "cg_attach_legacy", "errno=%d\n", errno))
 119                goto cleanup;
 120        detach_legacy = true;
 121
 122        links[last_cg] = bpf_program__attach_cgroup(skel->progs.egress,
 123                                                    cgs[last_cg].fd);
 124        if (CHECK(IS_ERR(links[last_cg]), "cg_attach", "err: %ld\n",
 125                  PTR_ERR(links[last_cg])))
 126                goto cleanup;
 127
 128        ping_and_check(cg_nr + 1, 0);
 129
 130        /* detach link */
 131        bpf_link__destroy(links[last_cg]);
 132        links[last_cg] = NULL;
 133
 134        /* detach legacy */
 135        err = bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
 136        if (CHECK(err, "cg_detach_legacy", "errno=%d\n", errno))
 137                goto cleanup;
 138        detach_legacy = false;
 139
 140        /* attach legacy exclusive prog attachment */
 141        err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
 142                              BPF_CGROUP_INET_EGRESS, 0);
 143        if (CHECK(err, "cg_attach_exclusive", "errno=%d\n", errno))
 144                goto cleanup;
 145        detach_legacy = true;
 146
 147        /* attempt to mix in with multi-attach bpf_link */
 148        tmp_link = bpf_program__attach_cgroup(skel->progs.egress,
 149                                              cgs[last_cg].fd);
 150        if (CHECK(!IS_ERR(tmp_link), "cg_attach_fail", "unexpected success!\n")) {
 151                bpf_link__destroy(tmp_link);
 152                goto cleanup;
 153        }
 154
 155        ping_and_check(cg_nr, 0);
 156
 157        /* detach */
 158        err = bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
 159        if (CHECK(err, "cg_detach_legacy", "errno=%d\n", errno))
 160                goto cleanup;
 161        detach_legacy = false;
 162
 163        ping_and_check(cg_nr - 1, 0);
 164
 165        /* attach back link-based one */
 166        links[last_cg] = bpf_program__attach_cgroup(skel->progs.egress,
 167                                                    cgs[last_cg].fd);
 168        if (CHECK(IS_ERR(links[last_cg]), "cg_attach", "err: %ld\n",
 169                  PTR_ERR(links[last_cg])))
 170                goto cleanup;
 171
 172        ping_and_check(cg_nr, 0);
 173
 174        /* check legacy exclusive prog can't be attached */
 175        err = bpf_prog_attach(prog_fd, cgs[last_cg].fd,
 176                              BPF_CGROUP_INET_EGRESS, 0);
 177        if (CHECK(!err, "cg_attach_exclusive", "unexpected success")) {
 178                bpf_prog_detach2(prog_fd, cgs[last_cg].fd, BPF_CGROUP_INET_EGRESS);
 179                goto cleanup;
 180        }
 181
 182        /* replace BPF programs inside their links for all but first link */
 183        for (i = 1; i < cg_nr; i++) {
 184                err = bpf_link__update_program(links[i], skel->progs.egress_alt);
 185                if (CHECK(err, "prog_upd", "link #%d\n", i))
 186                        goto cleanup;
 187        }
 188
 189        ping_and_check(1, cg_nr - 1);
 190
 191        /* Attempt program update with wrong expected BPF program */
 192        link_upd_opts.old_prog_fd = bpf_program__fd(skel->progs.egress_alt);
 193        link_upd_opts.flags = BPF_F_REPLACE;
 194        err = bpf_link_update(bpf_link__fd(links[0]),
 195                              bpf_program__fd(skel->progs.egress_alt),
 196                              &link_upd_opts);
 197        if (CHECK(err == 0 || errno != EPERM, "prog_cmpxchg1",
 198                  "unexpectedly succeeded, err %d, errno %d\n", err, -errno))
 199                goto cleanup;
 200
 201        /* Compare-exchange single link program from egress to egress_alt */
 202        link_upd_opts.old_prog_fd = bpf_program__fd(skel->progs.egress);
 203        link_upd_opts.flags = BPF_F_REPLACE;
 204        err = bpf_link_update(bpf_link__fd(links[0]),
 205                              bpf_program__fd(skel->progs.egress_alt),
 206                              &link_upd_opts);
 207        if (CHECK(err, "prog_cmpxchg2", "errno %d\n", -errno))
 208                goto cleanup;
 209
 210        /* ping */
 211        ping_and_check(0, cg_nr);
 212
 213        /* close cgroup FDs before detaching links */
 214        for (i = 0; i < cg_nr; i++) {
 215                if (cgs[i].fd > 0) {
 216                        close(cgs[i].fd);
 217                        cgs[i].fd = -1;
 218                }
 219        }
 220
 221        /* BPF programs should still get called */
 222        ping_and_check(0, cg_nr);
 223
 224        prog_id = link_info_prog_id(links[0], &info);
 225        CHECK(prog_id == 0, "link_info", "failed\n");
 226        CHECK(info.cgroup.cgroup_id == 0, "cgroup_id", "unexpected %llu\n", info.cgroup.cgroup_id);
 227
 228        err = bpf_link__detach(links[0]);
 229        if (CHECK(err, "link_detach", "failed %d\n", err))
 230                goto cleanup;
 231
 232        /* cgroup_id should be zero in link_info */
 233        prog_id = link_info_prog_id(links[0], &info);
 234        CHECK(prog_id == 0, "link_info", "failed\n");
 235        CHECK(info.cgroup.cgroup_id != 0, "cgroup_id", "unexpected %llu\n", info.cgroup.cgroup_id);
 236
 237        /* First BPF program shouldn't be called anymore */
 238        ping_and_check(0, cg_nr - 1);
 239
 240        /* leave cgroup and remove them, don't detach programs */
 241        cleanup_cgroup_environment();
 242
 243        /* BPF programs should have been auto-detached */
 244        ping_and_check(0, 0);
 245
 246cleanup:
 247        if (detach_legacy)
 248                bpf_prog_detach2(prog_fd, cgs[last_cg].fd,
 249                                 BPF_CGROUP_INET_EGRESS);
 250
 251        for (i = 0; i < cg_nr; i++) {
 252                if (!IS_ERR(links[i]))
 253                        bpf_link__destroy(links[i]);
 254        }
 255        test_cgroup_link__destroy(skel);
 256
 257        for (i = 0; i < cg_nr; i++) {
 258                if (cgs[i].fd > 0)
 259                        close(cgs[i].fd);
 260        }
 261        cleanup_cgroup_environment();
 262}
 263