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 20 static int duration; 21 22 static 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 40 static 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 57 static 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 78 out_clean: 79 close(client_fd); 80 close(server_fd); 81 return res; 82 } 83 84 static 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 (CHECK(IS_ERR(parent_link), "parent-cg-attach", 106 "err %ld", PTR_ERR(parent_link))) 107 goto close_bpf_object; 108 err = connect_send(CHILD_CGROUP); 109 if (CHECK(err, "first-connect-send", "errno %d", errno)) 110 goto close_bpf_object; 111 if (CHECK(obj->bss->invocations != 1, 112 "first-invoke", "invocations=%d", obj->bss->invocations)) 113 goto close_bpf_object; 114 key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP); 115 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 }; 116 if (assert_storage(obj->maps.cgroup_storage, 117 &key, &expected_cgroup_value)) 118 goto close_bpf_object; 119 key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP); 120 if (assert_storage_noexist(obj->maps.cgroup_storage, &key)) 121 goto close_bpf_object; 122 123 /* Attach to parent and child cgroup, trigger packet from child. 124 * Assert that there are two additional runs, one that run with parent 125 * cgroup's storage and one with child cgroup's storage. 126 */ 127 child_link = bpf_program__attach_cgroup(obj->progs.egress, 128 child_cgroup_fd); 129 if (CHECK(IS_ERR(child_link), "child-cg-attach", 130 "err %ld", PTR_ERR(child_link))) 131 goto close_bpf_object; 132 err = connect_send(CHILD_CGROUP); 133 if (CHECK(err, "second-connect-send", "errno %d", errno)) 134 goto close_bpf_object; 135 if (CHECK(obj->bss->invocations != 3, 136 "second-invoke", "invocations=%d", obj->bss->invocations)) 137 goto close_bpf_object; 138 key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP); 139 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 }; 140 if (assert_storage(obj->maps.cgroup_storage, 141 &key, &expected_cgroup_value)) 142 goto close_bpf_object; 143 key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP); 144 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 }; 145 if (assert_storage(obj->maps.cgroup_storage, 146 &key, &expected_cgroup_value)) 147 goto close_bpf_object; 148 149 close_bpf_object: 150 if (!IS_ERR(parent_link)) 151 bpf_link__destroy(parent_link); 152 if (!IS_ERR(child_link)) 153 bpf_link__destroy(child_link); 154 155 cg_storage_multi_egress_only__destroy(obj); 156 } 157 158 static 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 (CHECK(IS_ERR(parent_egress1_link), "parent-egress1-cg-attach", 180 "err %ld", PTR_ERR(parent_egress1_link))) 181 goto close_bpf_object; 182 parent_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2, 183 parent_cgroup_fd); 184 if (CHECK(IS_ERR(parent_egress2_link), "parent-egress2-cg-attach", 185 "err %ld", PTR_ERR(parent_egress2_link))) 186 goto close_bpf_object; 187 parent_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress, 188 parent_cgroup_fd); 189 if (CHECK(IS_ERR(parent_ingress_link), "parent-ingress-cg-attach", 190 "err %ld", PTR_ERR(parent_ingress_link))) 191 goto close_bpf_object; 192 err = connect_send(CHILD_CGROUP); 193 if (CHECK(err, "first-connect-send", "errno %d", errno)) 194 goto close_bpf_object; 195 if (CHECK(obj->bss->invocations != 3, 196 "first-invoke", "invocations=%d", obj->bss->invocations)) 197 goto close_bpf_object; 198 key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP); 199 key.attach_type = BPF_CGROUP_INET_EGRESS; 200 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 }; 201 if (assert_storage(obj->maps.cgroup_storage, 202 &key, &expected_cgroup_value)) 203 goto close_bpf_object; 204 key.attach_type = BPF_CGROUP_INET_INGRESS; 205 expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 1 }; 206 if (assert_storage(obj->maps.cgroup_storage, 207 &key, &expected_cgroup_value)) 208 goto close_bpf_object; 209 key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP); 210 key.attach_type = BPF_CGROUP_INET_EGRESS; 211 if (assert_storage_noexist(obj->maps.cgroup_storage, &key)) 212 goto close_bpf_object; 213 key.attach_type = BPF_CGROUP_INET_INGRESS; 214 if (assert_storage_noexist(obj->maps.cgroup_storage, &key)) 215 goto close_bpf_object; 216 217 /* Attach to parent and child cgroup, trigger packet from child. 218 * Assert that there is six additional runs, parent cgroup egresses and 219 * ingress, child cgroup egresses and ingress. 220 * Assert that egree and ingress storages are separate. 221 */ 222 child_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1, 223 child_cgroup_fd); 224 if (CHECK(IS_ERR(child_egress1_link), "child-egress1-cg-attach", 225 "err %ld", PTR_ERR(child_egress1_link))) 226 goto close_bpf_object; 227 child_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2, 228 child_cgroup_fd); 229 if (CHECK(IS_ERR(child_egress2_link), "child-egress2-cg-attach", 230 "err %ld", PTR_ERR(child_egress2_link))) 231 goto close_bpf_object; 232 child_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress, 233 child_cgroup_fd); 234 if (CHECK(IS_ERR(child_ingress_link), "child-ingress-cg-attach", 235 "err %ld", PTR_ERR(child_ingress_link))) 236 goto close_bpf_object; 237 err = connect_send(CHILD_CGROUP); 238 if (CHECK(err, "second-connect-send", "errno %d", errno)) 239 goto close_bpf_object; 240 if (CHECK(obj->bss->invocations != 9, 241 "second-invoke", "invocations=%d", obj->bss->invocations)) 242 goto close_bpf_object; 243 key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP); 244 key.attach_type = BPF_CGROUP_INET_EGRESS; 245 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 4 }; 246 if (assert_storage(obj->maps.cgroup_storage, 247 &key, &expected_cgroup_value)) 248 goto close_bpf_object; 249 key.attach_type = BPF_CGROUP_INET_INGRESS; 250 expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 2 }; 251 if (assert_storage(obj->maps.cgroup_storage, 252 &key, &expected_cgroup_value)) 253 goto close_bpf_object; 254 key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP); 255 key.attach_type = BPF_CGROUP_INET_EGRESS; 256 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 }; 257 if (assert_storage(obj->maps.cgroup_storage, 258 &key, &expected_cgroup_value)) 259 goto close_bpf_object; 260 key.attach_type = BPF_CGROUP_INET_INGRESS; 261 expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 1 }; 262 if (assert_storage(obj->maps.cgroup_storage, 263 &key, &expected_cgroup_value)) 264 goto close_bpf_object; 265 266 close_bpf_object: 267 if (!IS_ERR(parent_egress1_link)) 268 bpf_link__destroy(parent_egress1_link); 269 if (!IS_ERR(parent_egress2_link)) 270 bpf_link__destroy(parent_egress2_link); 271 if (!IS_ERR(parent_ingress_link)) 272 bpf_link__destroy(parent_ingress_link); 273 if (!IS_ERR(child_egress1_link)) 274 bpf_link__destroy(child_egress1_link); 275 if (!IS_ERR(child_egress2_link)) 276 bpf_link__destroy(child_egress2_link); 277 if (!IS_ERR(child_ingress_link)) 278 bpf_link__destroy(child_ingress_link); 279 280 cg_storage_multi_isolated__destroy(obj); 281 } 282 283 static void test_shared(int parent_cgroup_fd, int child_cgroup_fd) 284 { 285 struct cg_storage_multi_shared *obj; 286 struct cgroup_value expected_cgroup_value; 287 __u64 key; 288 struct bpf_link *parent_egress1_link = NULL, *parent_egress2_link = NULL; 289 struct bpf_link *child_egress1_link = NULL, *child_egress2_link = NULL; 290 struct bpf_link *parent_ingress_link = NULL, *child_ingress_link = NULL; 291 bool err; 292 293 obj = cg_storage_multi_shared__open_and_load(); 294 if (CHECK(!obj, "skel-load", "errno %d", errno)) 295 return; 296 297 /* Attach to parent cgroup, trigger packet from child. 298 * Assert that there is three runs, two with parent cgroup egress and 299 * one with parent cgroup ingress. 300 * Also assert that child cgroup's storage does not exist 301 */ 302 parent_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1, 303 parent_cgroup_fd); 304 if (CHECK(IS_ERR(parent_egress1_link), "parent-egress1-cg-attach", 305 "err %ld", PTR_ERR(parent_egress1_link))) 306 goto close_bpf_object; 307 parent_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2, 308 parent_cgroup_fd); 309 if (CHECK(IS_ERR(parent_egress2_link), "parent-egress2-cg-attach", 310 "err %ld", PTR_ERR(parent_egress2_link))) 311 goto close_bpf_object; 312 parent_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress, 313 parent_cgroup_fd); 314 if (CHECK(IS_ERR(parent_ingress_link), "parent-ingress-cg-attach", 315 "err %ld", PTR_ERR(parent_ingress_link))) 316 goto close_bpf_object; 317 err = connect_send(CHILD_CGROUP); 318 if (CHECK(err, "first-connect-send", "errno %d", errno)) 319 goto close_bpf_object; 320 if (CHECK(obj->bss->invocations != 3, 321 "first-invoke", "invocations=%d", obj->bss->invocations)) 322 goto close_bpf_object; 323 key = get_cgroup_id(PARENT_CGROUP); 324 expected_cgroup_value = (struct cgroup_value) { 325 .egress_pkts = 2, 326 .ingress_pkts = 1, 327 }; 328 if (assert_storage(obj->maps.cgroup_storage, 329 &key, &expected_cgroup_value)) 330 goto close_bpf_object; 331 key = get_cgroup_id(CHILD_CGROUP); 332 if (assert_storage_noexist(obj->maps.cgroup_storage, &key)) 333 goto close_bpf_object; 334 335 /* Attach to parent and child cgroup, trigger packet from child. 336 * Assert that there is six additional runs, parent cgroup egresses and 337 * ingress, child cgroup egresses and ingress. 338 */ 339 child_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1, 340 child_cgroup_fd); 341 if (CHECK(IS_ERR(child_egress1_link), "child-egress1-cg-attach", 342 "err %ld", PTR_ERR(child_egress1_link))) 343 goto close_bpf_object; 344 child_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2, 345 child_cgroup_fd); 346 if (CHECK(IS_ERR(child_egress2_link), "child-egress2-cg-attach", 347 "err %ld", PTR_ERR(child_egress2_link))) 348 goto close_bpf_object; 349 child_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress, 350 child_cgroup_fd); 351 if (CHECK(IS_ERR(child_ingress_link), "child-ingress-cg-attach", 352 "err %ld", PTR_ERR(child_ingress_link))) 353 goto close_bpf_object; 354 err = connect_send(CHILD_CGROUP); 355 if (CHECK(err, "second-connect-send", "errno %d", errno)) 356 goto close_bpf_object; 357 if (CHECK(obj->bss->invocations != 9, 358 "second-invoke", "invocations=%d", obj->bss->invocations)) 359 goto close_bpf_object; 360 key = get_cgroup_id(PARENT_CGROUP); 361 expected_cgroup_value = (struct cgroup_value) { 362 .egress_pkts = 4, 363 .ingress_pkts = 2, 364 }; 365 if (assert_storage(obj->maps.cgroup_storage, 366 &key, &expected_cgroup_value)) 367 goto close_bpf_object; 368 key = get_cgroup_id(CHILD_CGROUP); 369 expected_cgroup_value = (struct cgroup_value) { 370 .egress_pkts = 2, 371 .ingress_pkts = 1, 372 }; 373 if (assert_storage(obj->maps.cgroup_storage, 374 &key, &expected_cgroup_value)) 375 goto close_bpf_object; 376 377 close_bpf_object: 378 if (!IS_ERR(parent_egress1_link)) 379 bpf_link__destroy(parent_egress1_link); 380 if (!IS_ERR(parent_egress2_link)) 381 bpf_link__destroy(parent_egress2_link); 382 if (!IS_ERR(parent_ingress_link)) 383 bpf_link__destroy(parent_ingress_link); 384 if (!IS_ERR(child_egress1_link)) 385 bpf_link__destroy(child_egress1_link); 386 if (!IS_ERR(child_egress2_link)) 387 bpf_link__destroy(child_egress2_link); 388 if (!IS_ERR(child_ingress_link)) 389 bpf_link__destroy(child_ingress_link); 390 391 cg_storage_multi_shared__destroy(obj); 392 } 393 394 void test_cg_storage_multi(void) 395 { 396 int parent_cgroup_fd = -1, child_cgroup_fd = -1; 397 398 parent_cgroup_fd = test__join_cgroup(PARENT_CGROUP); 399 if (CHECK(parent_cgroup_fd < 0, "cg-create-parent", "errno %d", errno)) 400 goto close_cgroup_fd; 401 child_cgroup_fd = create_and_get_cgroup(CHILD_CGROUP); 402 if (CHECK(child_cgroup_fd < 0, "cg-create-child", "errno %d", errno)) 403 goto close_cgroup_fd; 404 405 if (test__start_subtest("egress_only")) 406 test_egress_only(parent_cgroup_fd, child_cgroup_fd); 407 408 if (test__start_subtest("isolated")) 409 test_isolated(parent_cgroup_fd, child_cgroup_fd); 410 411 if (test__start_subtest("shared")) 412 test_shared(parent_cgroup_fd, child_cgroup_fd); 413 414 close_cgroup_fd: 415 close(child_cgroup_fd); 416 close(parent_cgroup_fd); 417 } 418