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