1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "cgroup_helpers.h"
4 
5 #include "sockopt_multi.skel.h"
6 
7 static int run_getsockopt_test(struct sockopt_multi *obj, int cg_parent,
8 			       int cg_child, int sock_fd)
9 {
10 	struct bpf_link *link_parent = NULL;
11 	struct bpf_link *link_child = NULL;
12 	socklen_t optlen;
13 	__u8 buf;
14 	int err;
15 
16 	/* Set IP_TOS to the expected value (0x80). */
17 
18 	buf = 0x80;
19 	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
20 	if (err < 0) {
21 		log_err("Failed to call setsockopt(IP_TOS)");
22 		goto detach;
23 	}
24 
25 	buf = 0x00;
26 	optlen = 1;
27 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
28 	if (err) {
29 		log_err("Failed to call getsockopt(IP_TOS)");
30 		goto detach;
31 	}
32 
33 	if (buf != 0x80) {
34 		log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf);
35 		err = -1;
36 		goto detach;
37 	}
38 
39 	/* Attach child program and make sure it returns new value:
40 	 * - kernel:      -> 0x80
41 	 * - child:  0x80 -> 0x90
42 	 */
43 
44 	link_child = bpf_program__attach_cgroup(obj->progs._getsockopt_child,
45 						cg_child);
46 	if (!ASSERT_OK_PTR(link_child, "cg-attach-getsockopt_child"))
47 		goto detach;
48 
49 	buf = 0x00;
50 	optlen = 1;
51 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
52 	if (err) {
53 		log_err("Failed to call getsockopt(IP_TOS)");
54 		goto detach;
55 	}
56 
57 	if (buf != 0x90) {
58 		log_err("Unexpected getsockopt 0x%x != 0x90", buf);
59 		err = -1;
60 		goto detach;
61 	}
62 
63 	/* Attach parent program and make sure it returns new value:
64 	 * - kernel:      -> 0x80
65 	 * - child:  0x80 -> 0x90
66 	 * - parent: 0x90 -> 0xA0
67 	 */
68 
69 	link_parent = bpf_program__attach_cgroup(obj->progs._getsockopt_parent,
70 						 cg_parent);
71 	if (!ASSERT_OK_PTR(link_parent, "cg-attach-getsockopt_parent"))
72 		goto detach;
73 
74 	buf = 0x00;
75 	optlen = 1;
76 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
77 	if (err) {
78 		log_err("Failed to call getsockopt(IP_TOS)");
79 		goto detach;
80 	}
81 
82 	if (buf != 0xA0) {
83 		log_err("Unexpected getsockopt 0x%x != 0xA0", buf);
84 		err = -1;
85 		goto detach;
86 	}
87 
88 	/* Setting unexpected initial sockopt should return EPERM:
89 	 * - kernel: -> 0x40
90 	 * - child:  unexpected 0x40, EPERM
91 	 * - parent: unexpected 0x40, EPERM
92 	 */
93 
94 	buf = 0x40;
95 	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
96 	if (err < 0) {
97 		log_err("Failed to call setsockopt(IP_TOS)");
98 		goto detach;
99 	}
100 
101 	buf = 0x00;
102 	optlen = 1;
103 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
104 	if (!err) {
105 		log_err("Unexpected success from getsockopt(IP_TOS)");
106 		goto detach;
107 	}
108 
109 	/* Detach child program and make sure we still get EPERM:
110 	 * - kernel: -> 0x40
111 	 * - parent: unexpected 0x40, EPERM
112 	 */
113 
114 	bpf_link__destroy(link_child);
115 	link_child = NULL;
116 
117 	buf = 0x00;
118 	optlen = 1;
119 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
120 	if (!err) {
121 		log_err("Unexpected success from getsockopt(IP_TOS)");
122 		goto detach;
123 	}
124 
125 	/* Set initial value to the one the parent program expects:
126 	 * - kernel:      -> 0x90
127 	 * - parent: 0x90 -> 0xA0
128 	 */
129 
130 	buf = 0x90;
131 	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
132 	if (err < 0) {
133 		log_err("Failed to call setsockopt(IP_TOS)");
134 		goto detach;
135 	}
136 
137 	buf = 0x00;
138 	optlen = 1;
139 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
140 	if (err) {
141 		log_err("Failed to call getsockopt(IP_TOS)");
142 		goto detach;
143 	}
144 
145 	if (buf != 0xA0) {
146 		log_err("Unexpected getsockopt 0x%x != 0xA0", buf);
147 		err = -1;
148 		goto detach;
149 	}
150 
151 detach:
152 	bpf_link__destroy(link_child);
153 	bpf_link__destroy(link_parent);
154 
155 	return err;
156 }
157 
158 static int run_setsockopt_test(struct sockopt_multi *obj, int cg_parent,
159 			       int cg_child, int sock_fd)
160 {
161 	struct bpf_link *link_parent = NULL;
162 	struct bpf_link *link_child = NULL;
163 	socklen_t optlen;
164 	__u8 buf;
165 	int err;
166 
167 	/* Set IP_TOS to the expected value (0x80). */
168 
169 	buf = 0x80;
170 	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
171 	if (err < 0) {
172 		log_err("Failed to call setsockopt(IP_TOS)");
173 		goto detach;
174 	}
175 
176 	buf = 0x00;
177 	optlen = 1;
178 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
179 	if (err) {
180 		log_err("Failed to call getsockopt(IP_TOS)");
181 		goto detach;
182 	}
183 
184 	if (buf != 0x80) {
185 		log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf);
186 		err = -1;
187 		goto detach;
188 	}
189 
190 	/* Attach child program and make sure it adds 0x10. */
191 
192 	link_child = bpf_program__attach_cgroup(obj->progs._setsockopt,
193 						cg_child);
194 	if (!ASSERT_OK_PTR(link_child, "cg-attach-setsockopt_child"))
195 		goto detach;
196 
197 	buf = 0x80;
198 	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
199 	if (err < 0) {
200 		log_err("Failed to call setsockopt(IP_TOS)");
201 		goto detach;
202 	}
203 
204 	buf = 0x00;
205 	optlen = 1;
206 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
207 	if (err) {
208 		log_err("Failed to call getsockopt(IP_TOS)");
209 		goto detach;
210 	}
211 
212 	if (buf != 0x80 + 0x10) {
213 		log_err("Unexpected getsockopt 0x%x != 0x80 + 0x10", buf);
214 		err = -1;
215 		goto detach;
216 	}
217 
218 	/* Attach parent program and make sure it adds another 0x10. */
219 
220 	link_parent = bpf_program__attach_cgroup(obj->progs._setsockopt,
221 						 cg_parent);
222 	if (!ASSERT_OK_PTR(link_parent, "cg-attach-setsockopt_parent"))
223 		goto detach;
224 
225 	buf = 0x80;
226 	err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
227 	if (err < 0) {
228 		log_err("Failed to call setsockopt(IP_TOS)");
229 		goto detach;
230 	}
231 
232 	buf = 0x00;
233 	optlen = 1;
234 	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
235 	if (err) {
236 		log_err("Failed to call getsockopt(IP_TOS)");
237 		goto detach;
238 	}
239 
240 	if (buf != 0x80 + 2 * 0x10) {
241 		log_err("Unexpected getsockopt 0x%x != 0x80 + 2 * 0x10", buf);
242 		err = -1;
243 		goto detach;
244 	}
245 
246 detach:
247 	bpf_link__destroy(link_child);
248 	bpf_link__destroy(link_parent);
249 
250 	return err;
251 }
252 
253 void test_sockopt_multi(void)
254 {
255 	int cg_parent = -1, cg_child = -1;
256 	struct sockopt_multi *obj = NULL;
257 	int sock_fd = -1;
258 
259 	cg_parent = test__join_cgroup("/parent");
260 	if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent"))
261 		goto out;
262 
263 	cg_child = test__join_cgroup("/parent/child");
264 	if (!ASSERT_GE(cg_child, 0, "join_cgroup /parent/child"))
265 		goto out;
266 
267 	obj = sockopt_multi__open_and_load();
268 	if (!ASSERT_OK_PTR(obj, "skel-load"))
269 		goto out;
270 
271 	obj->bss->page_size = sysconf(_SC_PAGESIZE);
272 
273 	sock_fd = socket(AF_INET, SOCK_STREAM, 0);
274 	if (!ASSERT_GE(sock_fd, 0, "socket"))
275 		goto out;
276 
277 	ASSERT_OK(run_getsockopt_test(obj, cg_parent, cg_child, sock_fd), "getsockopt_test");
278 	ASSERT_OK(run_setsockopt_test(obj, cg_parent, cg_child, sock_fd), "setsockopt_test");
279 
280 out:
281 	close(sock_fd);
282 	sockopt_multi__destroy(obj);
283 	close(cg_child);
284 	close(cg_parent);
285 }
286