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