1d4a89c1eSYiFei Zhu // SPDX-License-Identifier: GPL-2.0-only
2d4a89c1eSYiFei Zhu 
3d4a89c1eSYiFei Zhu /*
4d4a89c1eSYiFei Zhu  * Copyright 2020 Google LLC.
5d4a89c1eSYiFei Zhu  */
6d4a89c1eSYiFei Zhu 
7d4a89c1eSYiFei Zhu #include <test_progs.h>
8d4a89c1eSYiFei Zhu #include <cgroup_helpers.h>
9d4a89c1eSYiFei Zhu #include <network_helpers.h>
10d4a89c1eSYiFei Zhu 
119e5bd1f7SYiFei Zhu #include "progs/cg_storage_multi.h"
129e5bd1f7SYiFei Zhu 
13d4a89c1eSYiFei Zhu #include "cg_storage_multi_egress_only.skel.h"
149e5bd1f7SYiFei Zhu #include "cg_storage_multi_egress_ingress.skel.h"
15d4a89c1eSYiFei Zhu 
16d4a89c1eSYiFei Zhu #define PARENT_CGROUP "/cgroup_storage"
17d4a89c1eSYiFei Zhu #define CHILD_CGROUP "/cgroup_storage/child"
18d4a89c1eSYiFei Zhu 
19d4a89c1eSYiFei Zhu static int duration;
20d4a89c1eSYiFei Zhu 
21d4a89c1eSYiFei Zhu static bool assert_storage(struct bpf_map *map, const char *cgroup_path,
229e5bd1f7SYiFei Zhu 			   struct cgroup_value *expected)
23d4a89c1eSYiFei Zhu {
24d4a89c1eSYiFei Zhu 	struct bpf_cgroup_storage_key key = {0};
259e5bd1f7SYiFei Zhu 	struct cgroup_value value;
26d4a89c1eSYiFei Zhu 	int map_fd;
27d4a89c1eSYiFei Zhu 
28d4a89c1eSYiFei Zhu 	map_fd = bpf_map__fd(map);
29d4a89c1eSYiFei Zhu 
30d4a89c1eSYiFei Zhu 	key.cgroup_inode_id = get_cgroup_id(cgroup_path);
31d4a89c1eSYiFei Zhu 	key.attach_type = BPF_CGROUP_INET_EGRESS;
32d4a89c1eSYiFei Zhu 	if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) < 0,
33d4a89c1eSYiFei Zhu 		  "map-lookup", "errno %d", errno))
34d4a89c1eSYiFei Zhu 		return true;
359e5bd1f7SYiFei Zhu 	if (CHECK(memcmp(&value, expected, sizeof(struct cgroup_value)),
369e5bd1f7SYiFei Zhu 		  "assert-storage", "storages differ"))
37d4a89c1eSYiFei Zhu 		return true;
38d4a89c1eSYiFei Zhu 
39d4a89c1eSYiFei Zhu 	return false;
40d4a89c1eSYiFei Zhu }
41d4a89c1eSYiFei Zhu 
42d4a89c1eSYiFei Zhu static bool assert_storage_noexist(struct bpf_map *map, const char *cgroup_path)
43d4a89c1eSYiFei Zhu {
44d4a89c1eSYiFei Zhu 	struct bpf_cgroup_storage_key key = {0};
459e5bd1f7SYiFei Zhu 	struct cgroup_value value;
46d4a89c1eSYiFei Zhu 	int map_fd;
47d4a89c1eSYiFei Zhu 
48d4a89c1eSYiFei Zhu 	map_fd = bpf_map__fd(map);
49d4a89c1eSYiFei Zhu 
50d4a89c1eSYiFei Zhu 	key.cgroup_inode_id = get_cgroup_id(cgroup_path);
51d4a89c1eSYiFei Zhu 	key.attach_type = BPF_CGROUP_INET_EGRESS;
52d4a89c1eSYiFei Zhu 	if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) == 0,
53d4a89c1eSYiFei Zhu 		  "map-lookup", "succeeded, expected ENOENT"))
54d4a89c1eSYiFei Zhu 		return true;
55d4a89c1eSYiFei Zhu 	if (CHECK(errno != ENOENT,
56d4a89c1eSYiFei Zhu 		  "map-lookup", "errno %d, expected ENOENT", errno))
57d4a89c1eSYiFei Zhu 		return true;
58d4a89c1eSYiFei Zhu 
59d4a89c1eSYiFei Zhu 	return false;
60d4a89c1eSYiFei Zhu }
61d4a89c1eSYiFei Zhu 
62d4a89c1eSYiFei Zhu static bool connect_send(const char *cgroup_path)
63d4a89c1eSYiFei Zhu {
64d4a89c1eSYiFei Zhu 	bool res = true;
65d4a89c1eSYiFei Zhu 	int server_fd = -1, client_fd = -1;
66d4a89c1eSYiFei Zhu 
67d4a89c1eSYiFei Zhu 	if (join_cgroup(cgroup_path))
68d4a89c1eSYiFei Zhu 		goto out_clean;
69d4a89c1eSYiFei Zhu 
70d4a89c1eSYiFei Zhu 	server_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);
71d4a89c1eSYiFei Zhu 	if (server_fd < 0)
72d4a89c1eSYiFei Zhu 		goto out_clean;
73d4a89c1eSYiFei Zhu 
74d4a89c1eSYiFei Zhu 	client_fd = connect_to_fd(server_fd, 0);
75d4a89c1eSYiFei Zhu 	if (client_fd < 0)
76d4a89c1eSYiFei Zhu 		goto out_clean;
77d4a89c1eSYiFei Zhu 
78d4a89c1eSYiFei Zhu 	if (send(client_fd, "message", strlen("message"), 0) < 0)
79d4a89c1eSYiFei Zhu 		goto out_clean;
80d4a89c1eSYiFei Zhu 
81d4a89c1eSYiFei Zhu 	res = false;
82d4a89c1eSYiFei Zhu 
83d4a89c1eSYiFei Zhu out_clean:
84d4a89c1eSYiFei Zhu 	close(client_fd);
85d4a89c1eSYiFei Zhu 	close(server_fd);
86d4a89c1eSYiFei Zhu 	return res;
87d4a89c1eSYiFei Zhu }
88d4a89c1eSYiFei Zhu 
89d4a89c1eSYiFei Zhu static void test_egress_only(int parent_cgroup_fd, int child_cgroup_fd)
90d4a89c1eSYiFei Zhu {
91d4a89c1eSYiFei Zhu 	struct cg_storage_multi_egress_only *obj;
929e5bd1f7SYiFei Zhu 	struct cgroup_value expected_cgroup_value;
93d4a89c1eSYiFei Zhu 	struct bpf_link *parent_link = NULL, *child_link = NULL;
94d4a89c1eSYiFei Zhu 	bool err;
95d4a89c1eSYiFei Zhu 
96d4a89c1eSYiFei Zhu 	obj = cg_storage_multi_egress_only__open_and_load();
97d4a89c1eSYiFei Zhu 	if (CHECK(!obj, "skel-load", "errno %d", errno))
98d4a89c1eSYiFei Zhu 		return;
99d4a89c1eSYiFei Zhu 
100d4a89c1eSYiFei Zhu 	/* Attach to parent cgroup, trigger packet from child.
101d4a89c1eSYiFei Zhu 	 * Assert that there is only one run and in that run the storage is
102d4a89c1eSYiFei Zhu 	 * parent cgroup's storage.
103d4a89c1eSYiFei Zhu 	 * Also assert that child cgroup's storage does not exist
104d4a89c1eSYiFei Zhu 	 */
105d4a89c1eSYiFei Zhu 	parent_link = bpf_program__attach_cgroup(obj->progs.egress,
106d4a89c1eSYiFei Zhu 						 parent_cgroup_fd);
107d4a89c1eSYiFei Zhu 	if (CHECK(IS_ERR(parent_link), "parent-cg-attach",
108d4a89c1eSYiFei Zhu 		  "err %ld", PTR_ERR(parent_link)))
109d4a89c1eSYiFei Zhu 		goto close_bpf_object;
110d4a89c1eSYiFei Zhu 	err = connect_send(CHILD_CGROUP);
111d4a89c1eSYiFei Zhu 	if (CHECK(err, "first-connect-send", "errno %d", errno))
112d4a89c1eSYiFei Zhu 		goto close_bpf_object;
113d4a89c1eSYiFei Zhu 	if (CHECK(obj->bss->invocations != 1,
114d4a89c1eSYiFei Zhu 		  "first-invoke", "invocations=%d", obj->bss->invocations))
115d4a89c1eSYiFei Zhu 		goto close_bpf_object;
1169e5bd1f7SYiFei Zhu 	expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 };
1179e5bd1f7SYiFei Zhu 	if (assert_storage(obj->maps.cgroup_storage,
1189e5bd1f7SYiFei Zhu 			   PARENT_CGROUP, &expected_cgroup_value))
119d4a89c1eSYiFei Zhu 		goto close_bpf_object;
120d4a89c1eSYiFei Zhu 	if (assert_storage_noexist(obj->maps.cgroup_storage, CHILD_CGROUP))
121d4a89c1eSYiFei Zhu 		goto close_bpf_object;
122d4a89c1eSYiFei Zhu 
123d4a89c1eSYiFei Zhu 	/* Attach to parent and child cgroup, trigger packet from child.
124d4a89c1eSYiFei Zhu 	 * Assert that there are two additional runs, one that run with parent
125d4a89c1eSYiFei Zhu 	 * cgroup's storage and one with child cgroup's storage.
126d4a89c1eSYiFei Zhu 	 */
127d4a89c1eSYiFei Zhu 	child_link = bpf_program__attach_cgroup(obj->progs.egress,
128d4a89c1eSYiFei Zhu 						child_cgroup_fd);
129d4a89c1eSYiFei Zhu 	if (CHECK(IS_ERR(child_link), "child-cg-attach",
130d4a89c1eSYiFei Zhu 		  "err %ld", PTR_ERR(child_link)))
131d4a89c1eSYiFei Zhu 		goto close_bpf_object;
132d4a89c1eSYiFei Zhu 	err = connect_send(CHILD_CGROUP);
133d4a89c1eSYiFei Zhu 	if (CHECK(err, "second-connect-send", "errno %d", errno))
134d4a89c1eSYiFei Zhu 		goto close_bpf_object;
135d4a89c1eSYiFei Zhu 	if (CHECK(obj->bss->invocations != 3,
136d4a89c1eSYiFei Zhu 		  "second-invoke", "invocations=%d", obj->bss->invocations))
137d4a89c1eSYiFei Zhu 		goto close_bpf_object;
1389e5bd1f7SYiFei Zhu 	expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 };
1399e5bd1f7SYiFei Zhu 	if (assert_storage(obj->maps.cgroup_storage,
1409e5bd1f7SYiFei Zhu 			   PARENT_CGROUP, &expected_cgroup_value))
141d4a89c1eSYiFei Zhu 		goto close_bpf_object;
1429e5bd1f7SYiFei Zhu 	expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 };
1439e5bd1f7SYiFei Zhu 	if (assert_storage(obj->maps.cgroup_storage,
1449e5bd1f7SYiFei Zhu 			   CHILD_CGROUP, &expected_cgroup_value))
145d4a89c1eSYiFei Zhu 		goto close_bpf_object;
146d4a89c1eSYiFei Zhu 
147d4a89c1eSYiFei Zhu close_bpf_object:
148d4a89c1eSYiFei Zhu 	bpf_link__destroy(parent_link);
149d4a89c1eSYiFei Zhu 	bpf_link__destroy(child_link);
150d4a89c1eSYiFei Zhu 
151d4a89c1eSYiFei Zhu 	cg_storage_multi_egress_only__destroy(obj);
152d4a89c1eSYiFei Zhu }
153d4a89c1eSYiFei Zhu 
1549e5bd1f7SYiFei Zhu static void test_egress_ingress(int parent_cgroup_fd, int child_cgroup_fd)
1559e5bd1f7SYiFei Zhu {
1569e5bd1f7SYiFei Zhu 	struct cg_storage_multi_egress_ingress *obj;
1579e5bd1f7SYiFei Zhu 
1589e5bd1f7SYiFei Zhu 	/* Cannot load both programs due to verifier failure:
1599e5bd1f7SYiFei Zhu 	 * "only one cgroup storage of each type is allowed"
1609e5bd1f7SYiFei Zhu 	 */
1619e5bd1f7SYiFei Zhu 	obj = cg_storage_multi_egress_ingress__open_and_load();
1629e5bd1f7SYiFei Zhu 	CHECK(obj || errno != EBUSY,
1639e5bd1f7SYiFei Zhu 	      "skel-load", "errno %d, expected EBUSY", errno);
1649e5bd1f7SYiFei Zhu 
1659e5bd1f7SYiFei Zhu 	cg_storage_multi_egress_ingress__destroy(obj);
1669e5bd1f7SYiFei Zhu }
1679e5bd1f7SYiFei Zhu 
168d4a89c1eSYiFei Zhu void test_cg_storage_multi(void)
169d4a89c1eSYiFei Zhu {
170d4a89c1eSYiFei Zhu 	int parent_cgroup_fd = -1, child_cgroup_fd = -1;
171d4a89c1eSYiFei Zhu 
172d4a89c1eSYiFei Zhu 	parent_cgroup_fd = test__join_cgroup(PARENT_CGROUP);
173d4a89c1eSYiFei Zhu 	if (CHECK(parent_cgroup_fd < 0, "cg-create-parent", "errno %d", errno))
174d4a89c1eSYiFei Zhu 		goto close_cgroup_fd;
175d4a89c1eSYiFei Zhu 	child_cgroup_fd = create_and_get_cgroup(CHILD_CGROUP);
176d4a89c1eSYiFei Zhu 	if (CHECK(child_cgroup_fd < 0, "cg-create-child", "errno %d", errno))
177d4a89c1eSYiFei Zhu 		goto close_cgroup_fd;
178d4a89c1eSYiFei Zhu 
179d4a89c1eSYiFei Zhu 	if (test__start_subtest("egress_only"))
180d4a89c1eSYiFei Zhu 		test_egress_only(parent_cgroup_fd, child_cgroup_fd);
181d4a89c1eSYiFei Zhu 
1829e5bd1f7SYiFei Zhu 	if (test__start_subtest("egress_ingress"))
1839e5bd1f7SYiFei Zhu 		test_egress_ingress(parent_cgroup_fd, child_cgroup_fd);
1849e5bd1f7SYiFei Zhu 
185d4a89c1eSYiFei Zhu close_cgroup_fd:
186d4a89c1eSYiFei Zhu 	close(child_cgroup_fd);
187d4a89c1eSYiFei Zhu 	close(parent_cgroup_fd);
188d4a89c1eSYiFei Zhu }
189