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 
11d4a89c1eSYiFei Zhu #include "cg_storage_multi_egress_only.skel.h"
12d4a89c1eSYiFei Zhu 
13d4a89c1eSYiFei Zhu #define PARENT_CGROUP "/cgroup_storage"
14d4a89c1eSYiFei Zhu #define CHILD_CGROUP "/cgroup_storage/child"
15d4a89c1eSYiFei Zhu 
16d4a89c1eSYiFei Zhu static int duration;
17d4a89c1eSYiFei Zhu 
18d4a89c1eSYiFei Zhu static bool assert_storage(struct bpf_map *map, const char *cgroup_path,
19d4a89c1eSYiFei Zhu 			   __u32 expected)
20d4a89c1eSYiFei Zhu {
21d4a89c1eSYiFei Zhu 	struct bpf_cgroup_storage_key key = {0};
22d4a89c1eSYiFei Zhu 	__u32 value;
23d4a89c1eSYiFei Zhu 	int map_fd;
24d4a89c1eSYiFei Zhu 
25d4a89c1eSYiFei Zhu 	map_fd = bpf_map__fd(map);
26d4a89c1eSYiFei Zhu 
27d4a89c1eSYiFei Zhu 	key.cgroup_inode_id = get_cgroup_id(cgroup_path);
28d4a89c1eSYiFei Zhu 	key.attach_type = BPF_CGROUP_INET_EGRESS;
29d4a89c1eSYiFei Zhu 	if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) < 0,
30d4a89c1eSYiFei Zhu 		  "map-lookup", "errno %d", errno))
31d4a89c1eSYiFei Zhu 		return true;
32d4a89c1eSYiFei Zhu 	if (CHECK(value != expected,
33d4a89c1eSYiFei Zhu 		  "assert-storage", "got %u expected %u", value, expected))
34d4a89c1eSYiFei Zhu 		return true;
35d4a89c1eSYiFei Zhu 
36d4a89c1eSYiFei Zhu 	return false;
37d4a89c1eSYiFei Zhu }
38d4a89c1eSYiFei Zhu 
39d4a89c1eSYiFei Zhu static bool assert_storage_noexist(struct bpf_map *map, const char *cgroup_path)
40d4a89c1eSYiFei Zhu {
41d4a89c1eSYiFei Zhu 	struct bpf_cgroup_storage_key key = {0};
42d4a89c1eSYiFei Zhu 	__u32 value;
43d4a89c1eSYiFei Zhu 	int map_fd;
44d4a89c1eSYiFei Zhu 
45d4a89c1eSYiFei Zhu 	map_fd = bpf_map__fd(map);
46d4a89c1eSYiFei Zhu 
47d4a89c1eSYiFei Zhu 	key.cgroup_inode_id = get_cgroup_id(cgroup_path);
48d4a89c1eSYiFei Zhu 	key.attach_type = BPF_CGROUP_INET_EGRESS;
49d4a89c1eSYiFei Zhu 	if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) == 0,
50d4a89c1eSYiFei Zhu 		  "map-lookup", "succeeded, expected ENOENT"))
51d4a89c1eSYiFei Zhu 		return true;
52d4a89c1eSYiFei Zhu 	if (CHECK(errno != ENOENT,
53d4a89c1eSYiFei Zhu 		  "map-lookup", "errno %d, expected ENOENT", errno))
54d4a89c1eSYiFei Zhu 		return true;
55d4a89c1eSYiFei Zhu 
56d4a89c1eSYiFei Zhu 	return false;
57d4a89c1eSYiFei Zhu }
58d4a89c1eSYiFei Zhu 
59d4a89c1eSYiFei Zhu static bool connect_send(const char *cgroup_path)
60d4a89c1eSYiFei Zhu {
61d4a89c1eSYiFei Zhu 	bool res = true;
62d4a89c1eSYiFei Zhu 	int server_fd = -1, client_fd = -1;
63d4a89c1eSYiFei Zhu 
64d4a89c1eSYiFei Zhu 	if (join_cgroup(cgroup_path))
65d4a89c1eSYiFei Zhu 		goto out_clean;
66d4a89c1eSYiFei Zhu 
67d4a89c1eSYiFei Zhu 	server_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);
68d4a89c1eSYiFei Zhu 	if (server_fd < 0)
69d4a89c1eSYiFei Zhu 		goto out_clean;
70d4a89c1eSYiFei Zhu 
71d4a89c1eSYiFei Zhu 	client_fd = connect_to_fd(server_fd, 0);
72d4a89c1eSYiFei Zhu 	if (client_fd < 0)
73d4a89c1eSYiFei Zhu 		goto out_clean;
74d4a89c1eSYiFei Zhu 
75d4a89c1eSYiFei Zhu 	if (send(client_fd, "message", strlen("message"), 0) < 0)
76d4a89c1eSYiFei Zhu 		goto out_clean;
77d4a89c1eSYiFei Zhu 
78d4a89c1eSYiFei Zhu 	res = false;
79d4a89c1eSYiFei Zhu 
80d4a89c1eSYiFei Zhu out_clean:
81d4a89c1eSYiFei Zhu 	close(client_fd);
82d4a89c1eSYiFei Zhu 	close(server_fd);
83d4a89c1eSYiFei Zhu 	return res;
84d4a89c1eSYiFei Zhu }
85d4a89c1eSYiFei Zhu 
86d4a89c1eSYiFei Zhu static void test_egress_only(int parent_cgroup_fd, int child_cgroup_fd)
87d4a89c1eSYiFei Zhu {
88d4a89c1eSYiFei Zhu 	struct cg_storage_multi_egress_only *obj;
89d4a89c1eSYiFei Zhu 	struct bpf_link *parent_link = NULL, *child_link = NULL;
90d4a89c1eSYiFei Zhu 	bool err;
91d4a89c1eSYiFei Zhu 
92d4a89c1eSYiFei Zhu 	obj = cg_storage_multi_egress_only__open_and_load();
93d4a89c1eSYiFei Zhu 	if (CHECK(!obj, "skel-load", "errno %d", errno))
94d4a89c1eSYiFei Zhu 		return;
95d4a89c1eSYiFei Zhu 
96d4a89c1eSYiFei Zhu 	/* Attach to parent cgroup, trigger packet from child.
97d4a89c1eSYiFei Zhu 	 * Assert that there is only one run and in that run the storage is
98d4a89c1eSYiFei Zhu 	 * parent cgroup's storage.
99d4a89c1eSYiFei Zhu 	 * Also assert that child cgroup's storage does not exist
100d4a89c1eSYiFei Zhu 	 */
101d4a89c1eSYiFei Zhu 	parent_link = bpf_program__attach_cgroup(obj->progs.egress,
102d4a89c1eSYiFei Zhu 						 parent_cgroup_fd);
103d4a89c1eSYiFei Zhu 	if (CHECK(IS_ERR(parent_link), "parent-cg-attach",
104d4a89c1eSYiFei Zhu 		  "err %ld", PTR_ERR(parent_link)))
105d4a89c1eSYiFei Zhu 		goto close_bpf_object;
106d4a89c1eSYiFei Zhu 	err = connect_send(CHILD_CGROUP);
107d4a89c1eSYiFei Zhu 	if (CHECK(err, "first-connect-send", "errno %d", errno))
108d4a89c1eSYiFei Zhu 		goto close_bpf_object;
109d4a89c1eSYiFei Zhu 	if (CHECK(obj->bss->invocations != 1,
110d4a89c1eSYiFei Zhu 		  "first-invoke", "invocations=%d", obj->bss->invocations))
111d4a89c1eSYiFei Zhu 		goto close_bpf_object;
112d4a89c1eSYiFei Zhu 	if (assert_storage(obj->maps.cgroup_storage, PARENT_CGROUP, 1))
113d4a89c1eSYiFei Zhu 		goto close_bpf_object;
114d4a89c1eSYiFei Zhu 	if (assert_storage_noexist(obj->maps.cgroup_storage, CHILD_CGROUP))
115d4a89c1eSYiFei Zhu 		goto close_bpf_object;
116d4a89c1eSYiFei Zhu 
117d4a89c1eSYiFei Zhu 	/* Attach to parent and child cgroup, trigger packet from child.
118d4a89c1eSYiFei Zhu 	 * Assert that there are two additional runs, one that run with parent
119d4a89c1eSYiFei Zhu 	 * cgroup's storage and one with child cgroup's storage.
120d4a89c1eSYiFei Zhu 	 */
121d4a89c1eSYiFei Zhu 	child_link = bpf_program__attach_cgroup(obj->progs.egress,
122d4a89c1eSYiFei Zhu 						child_cgroup_fd);
123d4a89c1eSYiFei Zhu 	if (CHECK(IS_ERR(child_link), "child-cg-attach",
124d4a89c1eSYiFei Zhu 		  "err %ld", PTR_ERR(child_link)))
125d4a89c1eSYiFei Zhu 		goto close_bpf_object;
126d4a89c1eSYiFei Zhu 	err = connect_send(CHILD_CGROUP);
127d4a89c1eSYiFei Zhu 	if (CHECK(err, "second-connect-send", "errno %d", errno))
128d4a89c1eSYiFei Zhu 		goto close_bpf_object;
129d4a89c1eSYiFei Zhu 	if (CHECK(obj->bss->invocations != 3,
130d4a89c1eSYiFei Zhu 		  "second-invoke", "invocations=%d", obj->bss->invocations))
131d4a89c1eSYiFei Zhu 		goto close_bpf_object;
132d4a89c1eSYiFei Zhu 	if (assert_storage(obj->maps.cgroup_storage, PARENT_CGROUP, 2))
133d4a89c1eSYiFei Zhu 		goto close_bpf_object;
134d4a89c1eSYiFei Zhu 	if (assert_storage(obj->maps.cgroup_storage, CHILD_CGROUP, 1))
135d4a89c1eSYiFei Zhu 		goto close_bpf_object;
136d4a89c1eSYiFei Zhu 
137d4a89c1eSYiFei Zhu close_bpf_object:
138d4a89c1eSYiFei Zhu 	bpf_link__destroy(parent_link);
139d4a89c1eSYiFei Zhu 	bpf_link__destroy(child_link);
140d4a89c1eSYiFei Zhu 
141d4a89c1eSYiFei Zhu 	cg_storage_multi_egress_only__destroy(obj);
142d4a89c1eSYiFei Zhu }
143d4a89c1eSYiFei Zhu 
144d4a89c1eSYiFei Zhu void test_cg_storage_multi(void)
145d4a89c1eSYiFei Zhu {
146d4a89c1eSYiFei Zhu 	int parent_cgroup_fd = -1, child_cgroup_fd = -1;
147d4a89c1eSYiFei Zhu 
148d4a89c1eSYiFei Zhu 	parent_cgroup_fd = test__join_cgroup(PARENT_CGROUP);
149d4a89c1eSYiFei Zhu 	if (CHECK(parent_cgroup_fd < 0, "cg-create-parent", "errno %d", errno))
150d4a89c1eSYiFei Zhu 		goto close_cgroup_fd;
151d4a89c1eSYiFei Zhu 	child_cgroup_fd = create_and_get_cgroup(CHILD_CGROUP);
152d4a89c1eSYiFei Zhu 	if (CHECK(child_cgroup_fd < 0, "cg-create-child", "errno %d", errno))
153d4a89c1eSYiFei Zhu 		goto close_cgroup_fd;
154d4a89c1eSYiFei Zhu 
155d4a89c1eSYiFei Zhu 	if (test__start_subtest("egress_only"))
156d4a89c1eSYiFei Zhu 		test_egress_only(parent_cgroup_fd, child_cgroup_fd);
157d4a89c1eSYiFei Zhu 
158d4a89c1eSYiFei Zhu close_cgroup_fd:
159d4a89c1eSYiFei Zhu 	close(child_cgroup_fd);
160d4a89c1eSYiFei Zhu 	close(parent_cgroup_fd);
161d4a89c1eSYiFei Zhu }
162