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 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 is 104 * parent cgroup's storage. 105 * Also assert that child cgroup's storage does not exist 106 */ 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 parent 128 * 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 and 174 * one with parent cgroup ingress, stored in separate parent storages. 175 * Also assert that child cgroup's storages does not exist 176 */ 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 and 216 * ingress, child cgroup egresses and ingress. 217 * Assert that egree 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 and 287 * one with parent cgroup ingress. 288 * Also assert that child cgroup's storage does not exist 289 */ 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 and 322 * 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