394 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 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 int server_fd = -1, client_fd = -1;60 char message[] = "message";61 bool res = true;62 63 if (join_cgroup(cgroup_path))64 goto out_clean;65 66 server_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);67 if (server_fd < 0)68 goto out_clean;69 70 client_fd = connect_to_fd(server_fd, 0);71 if (client_fd < 0)72 goto out_clean;73 74 if (send(client_fd, &message, sizeof(message), 0) < 0)75 goto out_clean;76 77 if (read(server_fd, &message, sizeof(message)) < 0)78 goto out_clean;79 80 res = false;81 82out_clean:83 close(client_fd);84 close(server_fd);85 return res;86}87 88static void test_egress_only(int parent_cgroup_fd, int child_cgroup_fd)89{90 struct cg_storage_multi_egress_only *obj;91 struct cgroup_value expected_cgroup_value;92 struct bpf_cgroup_storage_key key;93 struct bpf_link *parent_link = NULL, *child_link = NULL;94 bool err;95 96 key.attach_type = BPF_CGROUP_INET_EGRESS;97 98 obj = cg_storage_multi_egress_only__open_and_load();99 if (CHECK(!obj, "skel-load", "errno %d", errno))100 return;101 102 /* Attach to parent cgroup, trigger packet from child.103 * Assert that there is only one run and in that run the storage is104 * parent cgroup's storage.105 * Also assert that child cgroup's storage does not exist106 */107 parent_link = bpf_program__attach_cgroup(obj->progs.egress,108 parent_cgroup_fd);109 if (!ASSERT_OK_PTR(parent_link, "parent-cg-attach"))110 goto close_bpf_object;111 err = connect_send(CHILD_CGROUP);112 if (CHECK(err, "first-connect-send", "errno %d", errno))113 goto close_bpf_object;114 if (CHECK(obj->bss->invocations != 1,115 "first-invoke", "invocations=%d", obj->bss->invocations))116 goto close_bpf_object;117 key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP);118 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 };119 if (assert_storage(obj->maps.cgroup_storage,120 &key, &expected_cgroup_value))121 goto close_bpf_object;122 key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP);123 if (assert_storage_noexist(obj->maps.cgroup_storage, &key))124 goto close_bpf_object;125 126 /* Attach to parent and child cgroup, trigger packet from child.127 * Assert that there are two additional runs, one that run with parent128 * cgroup's storage and one with child cgroup's storage.129 */130 child_link = bpf_program__attach_cgroup(obj->progs.egress,131 child_cgroup_fd);132 if (!ASSERT_OK_PTR(child_link, "child-cg-attach"))133 goto close_bpf_object;134 err = connect_send(CHILD_CGROUP);135 if (CHECK(err, "second-connect-send", "errno %d", errno))136 goto close_bpf_object;137 if (CHECK(obj->bss->invocations != 3,138 "second-invoke", "invocations=%d", obj->bss->invocations))139 goto close_bpf_object;140 key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP);141 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 };142 if (assert_storage(obj->maps.cgroup_storage,143 &key, &expected_cgroup_value))144 goto close_bpf_object;145 key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP);146 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 };147 if (assert_storage(obj->maps.cgroup_storage,148 &key, &expected_cgroup_value))149 goto close_bpf_object;150 151close_bpf_object:152 bpf_link__destroy(parent_link);153 bpf_link__destroy(child_link);154 155 cg_storage_multi_egress_only__destroy(obj);156}157 158static void test_isolated(int parent_cgroup_fd, int child_cgroup_fd)159{160 struct cg_storage_multi_isolated *obj;161 struct cgroup_value expected_cgroup_value;162 struct bpf_cgroup_storage_key key;163 struct bpf_link *parent_egress1_link = NULL, *parent_egress2_link = NULL;164 struct bpf_link *child_egress1_link = NULL, *child_egress2_link = NULL;165 struct bpf_link *parent_ingress_link = NULL, *child_ingress_link = NULL;166 bool err;167 168 obj = cg_storage_multi_isolated__open_and_load();169 if (CHECK(!obj, "skel-load", "errno %d", errno))170 return;171 172 /* Attach to parent cgroup, trigger packet from child.173 * Assert that there is three runs, two with parent cgroup egress and174 * one with parent cgroup ingress, stored in separate parent storages.175 * Also assert that child cgroup's storages does not exist176 */177 parent_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1,178 parent_cgroup_fd);179 if (!ASSERT_OK_PTR(parent_egress1_link, "parent-egress1-cg-attach"))180 goto close_bpf_object;181 parent_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2,182 parent_cgroup_fd);183 if (!ASSERT_OK_PTR(parent_egress2_link, "parent-egress2-cg-attach"))184 goto close_bpf_object;185 parent_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress,186 parent_cgroup_fd);187 if (!ASSERT_OK_PTR(parent_ingress_link, "parent-ingress-cg-attach"))188 goto close_bpf_object;189 err = connect_send(CHILD_CGROUP);190 if (CHECK(err, "first-connect-send", "errno %d", errno))191 goto close_bpf_object;192 if (CHECK(obj->bss->invocations != 3,193 "first-invoke", "invocations=%d", obj->bss->invocations))194 goto close_bpf_object;195 key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP);196 key.attach_type = BPF_CGROUP_INET_EGRESS;197 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 };198 if (assert_storage(obj->maps.cgroup_storage,199 &key, &expected_cgroup_value))200 goto close_bpf_object;201 key.attach_type = BPF_CGROUP_INET_INGRESS;202 expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 1 };203 if (assert_storage(obj->maps.cgroup_storage,204 &key, &expected_cgroup_value))205 goto close_bpf_object;206 key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP);207 key.attach_type = BPF_CGROUP_INET_EGRESS;208 if (assert_storage_noexist(obj->maps.cgroup_storage, &key))209 goto close_bpf_object;210 key.attach_type = BPF_CGROUP_INET_INGRESS;211 if (assert_storage_noexist(obj->maps.cgroup_storage, &key))212 goto close_bpf_object;213 214 /* Attach to parent and child cgroup, trigger packet from child.215 * Assert that there is six additional runs, parent cgroup egresses and216 * ingress, child cgroup egresses and ingress.217 * Assert that egress and ingress storages are separate.218 */219 child_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1,220 child_cgroup_fd);221 if (!ASSERT_OK_PTR(child_egress1_link, "child-egress1-cg-attach"))222 goto close_bpf_object;223 child_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2,224 child_cgroup_fd);225 if (!ASSERT_OK_PTR(child_egress2_link, "child-egress2-cg-attach"))226 goto close_bpf_object;227 child_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress,228 child_cgroup_fd);229 if (!ASSERT_OK_PTR(child_ingress_link, "child-ingress-cg-attach"))230 goto close_bpf_object;231 err = connect_send(CHILD_CGROUP);232 if (CHECK(err, "second-connect-send", "errno %d", errno))233 goto close_bpf_object;234 if (CHECK(obj->bss->invocations != 9,235 "second-invoke", "invocations=%d", obj->bss->invocations))236 goto close_bpf_object;237 key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP);238 key.attach_type = BPF_CGROUP_INET_EGRESS;239 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 4 };240 if (assert_storage(obj->maps.cgroup_storage,241 &key, &expected_cgroup_value))242 goto close_bpf_object;243 key.attach_type = BPF_CGROUP_INET_INGRESS;244 expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 2 };245 if (assert_storage(obj->maps.cgroup_storage,246 &key, &expected_cgroup_value))247 goto close_bpf_object;248 key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP);249 key.attach_type = BPF_CGROUP_INET_EGRESS;250 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 };251 if (assert_storage(obj->maps.cgroup_storage,252 &key, &expected_cgroup_value))253 goto close_bpf_object;254 key.attach_type = BPF_CGROUP_INET_INGRESS;255 expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 1 };256 if (assert_storage(obj->maps.cgroup_storage,257 &key, &expected_cgroup_value))258 goto close_bpf_object;259 260close_bpf_object:261 bpf_link__destroy(parent_egress1_link);262 bpf_link__destroy(parent_egress2_link);263 bpf_link__destroy(parent_ingress_link);264 bpf_link__destroy(child_egress1_link);265 bpf_link__destroy(child_egress2_link);266 bpf_link__destroy(child_ingress_link);267 268 cg_storage_multi_isolated__destroy(obj);269}270 271static void test_shared(int parent_cgroup_fd, int child_cgroup_fd)272{273 struct cg_storage_multi_shared *obj;274 struct cgroup_value expected_cgroup_value;275 __u64 key;276 struct bpf_link *parent_egress1_link = NULL, *parent_egress2_link = NULL;277 struct bpf_link *child_egress1_link = NULL, *child_egress2_link = NULL;278 struct bpf_link *parent_ingress_link = NULL, *child_ingress_link = NULL;279 bool err;280 281 obj = cg_storage_multi_shared__open_and_load();282 if (CHECK(!obj, "skel-load", "errno %d", errno))283 return;284 285 /* Attach to parent cgroup, trigger packet from child.286 * Assert that there is three runs, two with parent cgroup egress and287 * one with parent cgroup ingress.288 * Also assert that child cgroup's storage does not exist289 */290 parent_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1,291 parent_cgroup_fd);292 if (!ASSERT_OK_PTR(parent_egress1_link, "parent-egress1-cg-attach"))293 goto close_bpf_object;294 parent_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2,295 parent_cgroup_fd);296 if (!ASSERT_OK_PTR(parent_egress2_link, "parent-egress2-cg-attach"))297 goto close_bpf_object;298 parent_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress,299 parent_cgroup_fd);300 if (!ASSERT_OK_PTR(parent_ingress_link, "parent-ingress-cg-attach"))301 goto close_bpf_object;302 err = connect_send(CHILD_CGROUP);303 if (CHECK(err, "first-connect-send", "errno %d", errno))304 goto close_bpf_object;305 if (CHECK(obj->bss->invocations != 3,306 "first-invoke", "invocations=%d", obj->bss->invocations))307 goto close_bpf_object;308 key = get_cgroup_id(PARENT_CGROUP);309 expected_cgroup_value = (struct cgroup_value) {310 .egress_pkts = 2,311 .ingress_pkts = 1,312 };313 if (assert_storage(obj->maps.cgroup_storage,314 &key, &expected_cgroup_value))315 goto close_bpf_object;316 key = get_cgroup_id(CHILD_CGROUP);317 if (assert_storage_noexist(obj->maps.cgroup_storage, &key))318 goto close_bpf_object;319 320 /* Attach to parent and child cgroup, trigger packet from child.321 * Assert that there is six additional runs, parent cgroup egresses and322 * ingress, child cgroup egresses and ingress.323 */324 child_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1,325 child_cgroup_fd);326 if (!ASSERT_OK_PTR(child_egress1_link, "child-egress1-cg-attach"))327 goto close_bpf_object;328 child_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2,329 child_cgroup_fd);330 if (!ASSERT_OK_PTR(child_egress2_link, "child-egress2-cg-attach"))331 goto close_bpf_object;332 child_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress,333 child_cgroup_fd);334 if (!ASSERT_OK_PTR(child_ingress_link, "child-ingress-cg-attach"))335 goto close_bpf_object;336 err = connect_send(CHILD_CGROUP);337 if (CHECK(err, "second-connect-send", "errno %d", errno))338 goto close_bpf_object;339 if (CHECK(obj->bss->invocations != 9,340 "second-invoke", "invocations=%d", obj->bss->invocations))341 goto close_bpf_object;342 key = get_cgroup_id(PARENT_CGROUP);343 expected_cgroup_value = (struct cgroup_value) {344 .egress_pkts = 4,345 .ingress_pkts = 2,346 };347 if (assert_storage(obj->maps.cgroup_storage,348 &key, &expected_cgroup_value))349 goto close_bpf_object;350 key = get_cgroup_id(CHILD_CGROUP);351 expected_cgroup_value = (struct cgroup_value) {352 .egress_pkts = 2,353 .ingress_pkts = 1,354 };355 if (assert_storage(obj->maps.cgroup_storage,356 &key, &expected_cgroup_value))357 goto close_bpf_object;358 359close_bpf_object:360 bpf_link__destroy(parent_egress1_link);361 bpf_link__destroy(parent_egress2_link);362 bpf_link__destroy(parent_ingress_link);363 bpf_link__destroy(child_egress1_link);364 bpf_link__destroy(child_egress2_link);365 bpf_link__destroy(child_ingress_link);366 367 cg_storage_multi_shared__destroy(obj);368}369 370void serial_test_cg_storage_multi(void)371{372 int parent_cgroup_fd = -1, child_cgroup_fd = -1;373 374 parent_cgroup_fd = test__join_cgroup(PARENT_CGROUP);375 if (CHECK(parent_cgroup_fd < 0, "cg-create-parent", "errno %d", errno))376 goto close_cgroup_fd;377 child_cgroup_fd = create_and_get_cgroup(CHILD_CGROUP);378 if (CHECK(child_cgroup_fd < 0, "cg-create-child", "errno %d", errno))379 goto close_cgroup_fd;380 381 if (test__start_subtest("egress_only"))382 test_egress_only(parent_cgroup_fd, child_cgroup_fd);383 384 if (test__start_subtest("isolated"))385 test_isolated(parent_cgroup_fd, child_cgroup_fd);386 387 if (test__start_subtest("shared"))388 test_shared(parent_cgroup_fd, child_cgroup_fd);389 390close_cgroup_fd:391 close(child_cgroup_fd);392 close(parent_cgroup_fd);393}394