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_egress_ingress.skel.h" 15 16 #define PARENT_CGROUP "/cgroup_storage" 17 #define CHILD_CGROUP "/cgroup_storage/child" 18 19 static int duration; 20 21 static bool assert_storage(struct bpf_map *map, const char *cgroup_path, 22 struct cgroup_value *expected) 23 { 24 struct bpf_cgroup_storage_key key = {0}; 25 struct cgroup_value value; 26 int map_fd; 27 28 map_fd = bpf_map__fd(map); 29 30 key.cgroup_inode_id = get_cgroup_id(cgroup_path); 31 key.attach_type = BPF_CGROUP_INET_EGRESS; 32 if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) < 0, 33 "map-lookup", "errno %d", errno)) 34 return true; 35 if (CHECK(memcmp(&value, expected, sizeof(struct cgroup_value)), 36 "assert-storage", "storages differ")) 37 return true; 38 39 return false; 40 } 41 42 static bool assert_storage_noexist(struct bpf_map *map, const char *cgroup_path) 43 { 44 struct bpf_cgroup_storage_key key = {0}; 45 struct cgroup_value value; 46 int map_fd; 47 48 map_fd = bpf_map__fd(map); 49 50 key.cgroup_inode_id = get_cgroup_id(cgroup_path); 51 key.attach_type = BPF_CGROUP_INET_EGRESS; 52 if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) == 0, 53 "map-lookup", "succeeded, expected ENOENT")) 54 return true; 55 if (CHECK(errno != ENOENT, 56 "map-lookup", "errno %d, expected ENOENT", errno)) 57 return true; 58 59 return false; 60 } 61 62 static bool connect_send(const char *cgroup_path) 63 { 64 bool res = true; 65 int server_fd = -1, client_fd = -1; 66 67 if (join_cgroup(cgroup_path)) 68 goto out_clean; 69 70 server_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0); 71 if (server_fd < 0) 72 goto out_clean; 73 74 client_fd = connect_to_fd(server_fd, 0); 75 if (client_fd < 0) 76 goto out_clean; 77 78 if (send(client_fd, "message", strlen("message"), 0) < 0) 79 goto out_clean; 80 81 res = false; 82 83 out_clean: 84 close(client_fd); 85 close(server_fd); 86 return res; 87 } 88 89 static void test_egress_only(int parent_cgroup_fd, int child_cgroup_fd) 90 { 91 struct cg_storage_multi_egress_only *obj; 92 struct cgroup_value expected_cgroup_value; 93 struct bpf_link *parent_link = NULL, *child_link = NULL; 94 bool err; 95 96 obj = cg_storage_multi_egress_only__open_and_load(); 97 if (CHECK(!obj, "skel-load", "errno %d", errno)) 98 return; 99 100 /* Attach to parent cgroup, trigger packet from child. 101 * Assert that there is only one run and in that run the storage is 102 * parent cgroup's storage. 103 * Also assert that child cgroup's storage does not exist 104 */ 105 parent_link = bpf_program__attach_cgroup(obj->progs.egress, 106 parent_cgroup_fd); 107 if (CHECK(IS_ERR(parent_link), "parent-cg-attach", 108 "err %ld", PTR_ERR(parent_link))) 109 goto close_bpf_object; 110 err = connect_send(CHILD_CGROUP); 111 if (CHECK(err, "first-connect-send", "errno %d", errno)) 112 goto close_bpf_object; 113 if (CHECK(obj->bss->invocations != 1, 114 "first-invoke", "invocations=%d", obj->bss->invocations)) 115 goto close_bpf_object; 116 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 }; 117 if (assert_storage(obj->maps.cgroup_storage, 118 PARENT_CGROUP, &expected_cgroup_value)) 119 goto close_bpf_object; 120 if (assert_storage_noexist(obj->maps.cgroup_storage, CHILD_CGROUP)) 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 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 }; 139 if (assert_storage(obj->maps.cgroup_storage, 140 PARENT_CGROUP, &expected_cgroup_value)) 141 goto close_bpf_object; 142 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 }; 143 if (assert_storage(obj->maps.cgroup_storage, 144 CHILD_CGROUP, &expected_cgroup_value)) 145 goto close_bpf_object; 146 147 close_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 154 static void test_egress_ingress(int parent_cgroup_fd, int child_cgroup_fd) 155 { 156 struct cg_storage_multi_egress_ingress *obj; 157 158 /* Cannot load both programs due to verifier failure: 159 * "only one cgroup storage of each type is allowed" 160 */ 161 obj = cg_storage_multi_egress_ingress__open_and_load(); 162 CHECK(obj || errno != EBUSY, 163 "skel-load", "errno %d, expected EBUSY", errno); 164 165 cg_storage_multi_egress_ingress__destroy(obj); 166 } 167 168 void test_cg_storage_multi(void) 169 { 170 int parent_cgroup_fd = -1, child_cgroup_fd = -1; 171 172 parent_cgroup_fd = test__join_cgroup(PARENT_CGROUP); 173 if (CHECK(parent_cgroup_fd < 0, "cg-create-parent", "errno %d", errno)) 174 goto close_cgroup_fd; 175 child_cgroup_fd = create_and_get_cgroup(CHILD_CGROUP); 176 if (CHECK(child_cgroup_fd < 0, "cg-create-child", "errno %d", errno)) 177 goto close_cgroup_fd; 178 179 if (test__start_subtest("egress_only")) 180 test_egress_only(parent_cgroup_fd, child_cgroup_fd); 181 182 if (test__start_subtest("egress_ingress")) 183 test_egress_ingress(parent_cgroup_fd, child_cgroup_fd); 184 185 close_cgroup_fd: 186 close(child_cgroup_fd); 187 close(parent_cgroup_fd); 188 } 189