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