Linux Audio

Check our new training course

In-person Linux kernel drivers training

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