cg_storage_multi.c (13592B)
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 serial_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}