linux/tools/testing/selftests/bpf/prog_tests/cg_storage_multi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3/*
   4 * Copyright 2020 Google LLC.
   5 */
   6
   7#include <test_progs.h>
   8#include <cgroup_helpers.h>
   9#include <network_helpers.h>
  10
  11#include "progs/cg_storage_multi.h"
  12
  13#include "cg_storage_multi_egress_only.skel.h"
  14#include "cg_storage_multi_isolated.skel.h"
  15#include "cg_storage_multi_shared.skel.h"
  16
  17#define PARENT_CGROUP "/cgroup_storage"
  18#define CHILD_CGROUP "/cgroup_storage/child"
  19
  20static int duration;
  21
  22static bool assert_storage(struct bpf_map *map, const void *key,
  23                           struct cgroup_value *expected)
  24{
  25        struct cgroup_value value;
  26        int map_fd;
  27
  28        map_fd = bpf_map__fd(map);
  29
  30        if (CHECK(bpf_map_lookup_elem(map_fd, key, &value) < 0,
  31                  "map-lookup", "errno %d", errno))
  32                return true;
  33        if (CHECK(memcmp(&value, expected, sizeof(struct cgroup_value)),
  34                  "assert-storage", "storages differ"))
  35                return true;
  36
  37        return false;
  38}
  39
  40static bool assert_storage_noexist(struct bpf_map *map, const void *key)
  41{
  42        struct cgroup_value value;
  43        int map_fd;
  44
  45        map_fd = bpf_map__fd(map);
  46
  47        if (CHECK(bpf_map_lookup_elem(map_fd, key, &value) == 0,
  48                  "map-lookup", "succeeded, expected ENOENT"))
  49                return true;
  50        if (CHECK(errno != ENOENT,
  51                  "map-lookup", "errno %d, expected ENOENT", errno))
  52                return true;
  53
  54        return false;
  55}
  56
  57static bool connect_send(const char *cgroup_path)
  58{
  59        bool res = true;
  60        int server_fd = -1, client_fd = -1;
  61
  62        if (join_cgroup(cgroup_path))
  63                goto out_clean;
  64
  65        server_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);
  66        if (server_fd < 0)
  67                goto out_clean;
  68
  69        client_fd = connect_to_fd(server_fd, 0);
  70        if (client_fd < 0)
  71                goto out_clean;
  72
  73        if (send(client_fd, "message", strlen("message"), 0) < 0)
  74                goto out_clean;
  75
  76        res = false;
  77
  78out_clean:
  79        close(client_fd);
  80        close(server_fd);
  81        return res;
  82}
  83
  84static void test_egress_only(int parent_cgroup_fd, int child_cgroup_fd)
  85{
  86        struct cg_storage_multi_egress_only *obj;
  87        struct cgroup_value expected_cgroup_value;
  88        struct bpf_cgroup_storage_key key;
  89        struct bpf_link *parent_link = NULL, *child_link = NULL;
  90        bool err;
  91
  92        key.attach_type = BPF_CGROUP_INET_EGRESS;
  93
  94        obj = cg_storage_multi_egress_only__open_and_load();
  95        if (CHECK(!obj, "skel-load", "errno %d", errno))
  96                return;
  97
  98        /* Attach to parent cgroup, trigger packet from child.
  99         * Assert that there is only one run and in that run the storage is
 100         * parent cgroup's storage.
 101         * Also assert that child cgroup's storage does not exist
 102         */
 103        parent_link = bpf_program__attach_cgroup(obj->progs.egress,
 104                                                 parent_cgroup_fd);
 105        if (!ASSERT_OK_PTR(parent_link, "parent-cg-attach"))
 106                goto close_bpf_object;
 107        err = connect_send(CHILD_CGROUP);
 108        if (CHECK(err, "first-connect-send", "errno %d", errno))
 109                goto close_bpf_object;
 110        if (CHECK(obj->bss->invocations != 1,
 111                  "first-invoke", "invocations=%d", obj->bss->invocations))
 112                goto close_bpf_object;
 113        key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP);
 114        expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 };
 115        if (assert_storage(obj->maps.cgroup_storage,
 116                           &key, &expected_cgroup_value))
 117                goto close_bpf_object;
 118        key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP);
 119        if (assert_storage_noexist(obj->maps.cgroup_storage, &key))
 120                goto close_bpf_object;
 121
 122        /* Attach to parent and child cgroup, trigger packet from child.
 123         * Assert that there are two additional runs, one that run with parent
 124         * cgroup's storage and one with child cgroup's storage.
 125         */
 126        child_link = bpf_program__attach_cgroup(obj->progs.egress,
 127                                                child_cgroup_fd);
 128        if (!ASSERT_OK_PTR(child_link, "child-cg-attach"))
 129                goto close_bpf_object;
 130        err = connect_send(CHILD_CGROUP);
 131        if (CHECK(err, "second-connect-send", "errno %d", errno))
 132                goto close_bpf_object;
 133        if (CHECK(obj->bss->invocations != 3,
 134                  "second-invoke", "invocations=%d", obj->bss->invocations))
 135                goto close_bpf_object;
 136        key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP);
 137        expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 };
 138        if (assert_storage(obj->maps.cgroup_storage,
 139                           &key, &expected_cgroup_value))
 140                goto close_bpf_object;
 141        key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP);
 142        expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 };
 143        if (assert_storage(obj->maps.cgroup_storage,
 144                           &key, &expected_cgroup_value))
 145                goto close_bpf_object;
 146
 147close_bpf_object:
 148        bpf_link__destroy(parent_link);
 149        bpf_link__destroy(child_link);
 150
 151        cg_storage_multi_egress_only__destroy(obj);
 152}
 153
 154static void test_isolated(int parent_cgroup_fd, int child_cgroup_fd)
 155{
 156        struct cg_storage_multi_isolated *obj;
 157        struct cgroup_value expected_cgroup_value;
 158        struct bpf_cgroup_storage_key key;
 159        struct bpf_link *parent_egress1_link = NULL, *parent_egress2_link = NULL;
 160        struct bpf_link *child_egress1_link = NULL, *child_egress2_link = NULL;
 161        struct bpf_link *parent_ingress_link = NULL, *child_ingress_link = NULL;
 162        bool err;
 163
 164        obj = cg_storage_multi_isolated__open_and_load();
 165        if (CHECK(!obj, "skel-load", "errno %d", errno))
 166                return;
 167
 168        /* Attach to parent cgroup, trigger packet from child.
 169         * Assert that there is three runs, two with parent cgroup egress and
 170         * one with parent cgroup ingress, stored in separate parent storages.
 171         * Also assert that child cgroup's storages does not exist
 172         */
 173        parent_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1,
 174                                                         parent_cgroup_fd);
 175        if (!ASSERT_OK_PTR(parent_egress1_link, "parent-egress1-cg-attach"))
 176                goto close_bpf_object;
 177        parent_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2,
 178                                                         parent_cgroup_fd);
 179        if (!ASSERT_OK_PTR(parent_egress2_link, "parent-egress2-cg-attach"))
 180                goto close_bpf_object;
 181        parent_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress,
 182                                                         parent_cgroup_fd);
 183        if (!ASSERT_OK_PTR(parent_ingress_link, "parent-ingress-cg-attach"))
 184                goto close_bpf_object;
 185        err = connect_send(CHILD_CGROUP);
 186        if (CHECK(err, "first-connect-send", "errno %d", errno))
 187                goto close_bpf_object;
 188        if (CHECK(obj->bss->invocations != 3,
 189                  "first-invoke", "invocations=%d", obj->bss->invocations))
 190                goto close_bpf_object;
 191        key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP);
 192        key.attach_type = BPF_CGROUP_INET_EGRESS;
 193        expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 };
 194        if (assert_storage(obj->maps.cgroup_storage,
 195                           &key, &expected_cgroup_value))
 196                goto close_bpf_object;
 197        key.attach_type = BPF_CGROUP_INET_INGRESS;
 198        expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 1 };
 199        if (assert_storage(obj->maps.cgroup_storage,
 200                           &key, &expected_cgroup_value))
 201                goto close_bpf_object;
 202        key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP);
 203        key.attach_type = BPF_CGROUP_INET_EGRESS;
 204        if (assert_storage_noexist(obj->maps.cgroup_storage, &key))
 205                goto close_bpf_object;
 206        key.attach_type = BPF_CGROUP_INET_INGRESS;
 207        if (assert_storage_noexist(obj->maps.cgroup_storage, &key))
 208                goto close_bpf_object;
 209
 210        /* Attach to parent and child cgroup, trigger packet from child.
 211         * Assert that there is six additional runs, parent cgroup egresses and
 212         * ingress, child cgroup egresses and ingress.
 213         * Assert that egree and ingress storages are separate.
 214         */
 215        child_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1,
 216                                                        child_cgroup_fd);
 217        if (!ASSERT_OK_PTR(child_egress1_link, "child-egress1-cg-attach"))
 218                goto close_bpf_object;
 219        child_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2,
 220                                                        child_cgroup_fd);
 221        if (!ASSERT_OK_PTR(child_egress2_link, "child-egress2-cg-attach"))
 222                goto close_bpf_object;
 223        child_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress,
 224                                                        child_cgroup_fd);
 225        if (!ASSERT_OK_PTR(child_ingress_link, "child-ingress-cg-attach"))
 226                goto close_bpf_object;
 227        err = connect_send(CHILD_CGROUP);
 228        if (CHECK(err, "second-connect-send", "errno %d", errno))
 229                goto close_bpf_object;
 230        if (CHECK(obj->bss->invocations != 9,
 231                  "second-invoke", "invocations=%d", obj->bss->invocations))
 232                goto close_bpf_object;
 233        key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP);
 234        key.attach_type = BPF_CGROUP_INET_EGRESS;
 235        expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 4 };
 236        if (assert_storage(obj->maps.cgroup_storage,
 237                           &key, &expected_cgroup_value))
 238                goto close_bpf_object;
 239        key.attach_type = BPF_CGROUP_INET_INGRESS;
 240        expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 2 };
 241        if (assert_storage(obj->maps.cgroup_storage,
 242                           &key, &expected_cgroup_value))
 243                goto close_bpf_object;
 244        key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP);
 245        key.attach_type = BPF_CGROUP_INET_EGRESS;
 246        expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 };
 247        if (assert_storage(obj->maps.cgroup_storage,
 248                           &key, &expected_cgroup_value))
 249                goto close_bpf_object;
 250        key.attach_type = BPF_CGROUP_INET_INGRESS;
 251        expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 1 };
 252        if (assert_storage(obj->maps.cgroup_storage,
 253                           &key, &expected_cgroup_value))
 254                goto close_bpf_object;
 255
 256close_bpf_object:
 257        bpf_link__destroy(parent_egress1_link);
 258        bpf_link__destroy(parent_egress2_link);
 259        bpf_link__destroy(parent_ingress_link);
 260        bpf_link__destroy(child_egress1_link);
 261        bpf_link__destroy(child_egress2_link);
 262        bpf_link__destroy(child_ingress_link);
 263
 264        cg_storage_multi_isolated__destroy(obj);
 265}
 266
 267static void test_shared(int parent_cgroup_fd, int child_cgroup_fd)
 268{
 269        struct cg_storage_multi_shared *obj;
 270        struct cgroup_value expected_cgroup_value;
 271        __u64 key;
 272        struct bpf_link *parent_egress1_link = NULL, *parent_egress2_link = NULL;
 273        struct bpf_link *child_egress1_link = NULL, *child_egress2_link = NULL;
 274        struct bpf_link *parent_ingress_link = NULL, *child_ingress_link = NULL;
 275        bool err;
 276
 277        obj = cg_storage_multi_shared__open_and_load();
 278        if (CHECK(!obj, "skel-load", "errno %d", errno))
 279                return;
 280
 281        /* Attach to parent cgroup, trigger packet from child.
 282         * Assert that there is three runs, two with parent cgroup egress and
 283         * one with parent cgroup ingress.
 284         * Also assert that child cgroup's storage does not exist
 285         */
 286        parent_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1,
 287                                                         parent_cgroup_fd);
 288        if (!ASSERT_OK_PTR(parent_egress1_link, "parent-egress1-cg-attach"))
 289                goto close_bpf_object;
 290        parent_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2,
 291                                                         parent_cgroup_fd);
 292        if (!ASSERT_OK_PTR(parent_egress2_link, "parent-egress2-cg-attach"))
 293                goto close_bpf_object;
 294        parent_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress,
 295                                                         parent_cgroup_fd);
 296        if (!ASSERT_OK_PTR(parent_ingress_link, "parent-ingress-cg-attach"))
 297                goto close_bpf_object;
 298        err = connect_send(CHILD_CGROUP);
 299        if (CHECK(err, "first-connect-send", "errno %d", errno))
 300                goto close_bpf_object;
 301        if (CHECK(obj->bss->invocations != 3,
 302                  "first-invoke", "invocations=%d", obj->bss->invocations))
 303                goto close_bpf_object;
 304        key = get_cgroup_id(PARENT_CGROUP);
 305        expected_cgroup_value = (struct cgroup_value) {
 306                .egress_pkts = 2,
 307                .ingress_pkts = 1,
 308        };
 309        if (assert_storage(obj->maps.cgroup_storage,
 310                           &key, &expected_cgroup_value))
 311                goto close_bpf_object;
 312        key = get_cgroup_id(CHILD_CGROUP);
 313        if (assert_storage_noexist(obj->maps.cgroup_storage, &key))
 314                goto close_bpf_object;
 315
 316        /* Attach to parent and child cgroup, trigger packet from child.
 317         * Assert that there is six additional runs, parent cgroup egresses and
 318         * ingress, child cgroup egresses and ingress.
 319         */
 320        child_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1,
 321                                                        child_cgroup_fd);
 322        if (!ASSERT_OK_PTR(child_egress1_link, "child-egress1-cg-attach"))
 323                goto close_bpf_object;
 324        child_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2,
 325                                                        child_cgroup_fd);
 326        if (!ASSERT_OK_PTR(child_egress2_link, "child-egress2-cg-attach"))
 327                goto close_bpf_object;
 328        child_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress,
 329                                                        child_cgroup_fd);
 330        if (!ASSERT_OK_PTR(child_ingress_link, "child-ingress-cg-attach"))
 331                goto close_bpf_object;
 332        err = connect_send(CHILD_CGROUP);
 333        if (CHECK(err, "second-connect-send", "errno %d", errno))
 334                goto close_bpf_object;
 335        if (CHECK(obj->bss->invocations != 9,
 336                  "second-invoke", "invocations=%d", obj->bss->invocations))
 337                goto close_bpf_object;
 338        key = get_cgroup_id(PARENT_CGROUP);
 339        expected_cgroup_value = (struct cgroup_value) {
 340                .egress_pkts = 4,
 341                .ingress_pkts = 2,
 342        };
 343        if (assert_storage(obj->maps.cgroup_storage,
 344                           &key, &expected_cgroup_value))
 345                goto close_bpf_object;
 346        key = get_cgroup_id(CHILD_CGROUP);
 347        expected_cgroup_value = (struct cgroup_value) {
 348                .egress_pkts = 2,
 349                .ingress_pkts = 1,
 350        };
 351        if (assert_storage(obj->maps.cgroup_storage,
 352                           &key, &expected_cgroup_value))
 353                goto close_bpf_object;
 354
 355close_bpf_object:
 356        bpf_link__destroy(parent_egress1_link);
 357        bpf_link__destroy(parent_egress2_link);
 358        bpf_link__destroy(parent_ingress_link);
 359        bpf_link__destroy(child_egress1_link);
 360        bpf_link__destroy(child_egress2_link);
 361        bpf_link__destroy(child_ingress_link);
 362
 363        cg_storage_multi_shared__destroy(obj);
 364}
 365
 366void test_cg_storage_multi(void)
 367{
 368        int parent_cgroup_fd = -1, child_cgroup_fd = -1;
 369
 370        parent_cgroup_fd = test__join_cgroup(PARENT_CGROUP);
 371        if (CHECK(parent_cgroup_fd < 0, "cg-create-parent", "errno %d", errno))
 372                goto close_cgroup_fd;
 373        child_cgroup_fd = create_and_get_cgroup(CHILD_CGROUP);
 374        if (CHECK(child_cgroup_fd < 0, "cg-create-child", "errno %d", errno))
 375                goto close_cgroup_fd;
 376
 377        if (test__start_subtest("egress_only"))
 378                test_egress_only(parent_cgroup_fd, child_cgroup_fd);
 379
 380        if (test__start_subtest("isolated"))
 381                test_isolated(parent_cgroup_fd, child_cgroup_fd);
 382
 383        if (test__start_subtest("shared"))
 384                test_shared(parent_cgroup_fd, child_cgroup_fd);
 385
 386close_cgroup_fd:
 387        close(child_cgroup_fd);
 388        close(parent_cgroup_fd);
 389}
 390