1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "cgroup_helpers.h"
4
prog_attach(struct bpf_object * obj,int cgroup_fd,const char * title)5 static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title)
6 {
7 enum bpf_attach_type attach_type;
8 enum bpf_prog_type prog_type;
9 struct bpf_program *prog;
10 int err;
11
12 err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
13 if (err) {
14 log_err("Failed to deduct types for %s BPF program", title);
15 return -1;
16 }
17
18 prog = bpf_object__find_program_by_title(obj, title);
19 if (!prog) {
20 log_err("Failed to find %s BPF program", title);
21 return -1;
22 }
23
24 err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
25 attach_type, BPF_F_ALLOW_MULTI);
26 if (err) {
27 log_err("Failed to attach %s BPF program", title);
28 return -1;
29 }
30
31 return 0;
32 }
33
prog_detach(struct bpf_object * obj,int cgroup_fd,const char * title)34 static int prog_detach(struct bpf_object *obj, int cgroup_fd, const char *title)
35 {
36 enum bpf_attach_type attach_type;
37 enum bpf_prog_type prog_type;
38 struct bpf_program *prog;
39 int err;
40
41 err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
42 if (err)
43 return -1;
44
45 prog = bpf_object__find_program_by_title(obj, title);
46 if (!prog)
47 return -1;
48
49 err = bpf_prog_detach2(bpf_program__fd(prog), cgroup_fd,
50 attach_type);
51 if (err)
52 return -1;
53
54 return 0;
55 }
56
run_getsockopt_test(struct bpf_object * obj,int cg_parent,int cg_child,int sock_fd)57 static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
58 int cg_child, int sock_fd)
59 {
60 socklen_t optlen;
61 __u8 buf;
62 int err;
63
64 /* Set IP_TOS to the expected value (0x80). */
65
66 buf = 0x80;
67 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
68 if (err < 0) {
69 log_err("Failed to call setsockopt(IP_TOS)");
70 goto detach;
71 }
72
73 buf = 0x00;
74 optlen = 1;
75 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
76 if (err) {
77 log_err("Failed to call getsockopt(IP_TOS)");
78 goto detach;
79 }
80
81 if (buf != 0x80) {
82 log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf);
83 err = -1;
84 goto detach;
85 }
86
87 /* Attach child program and make sure it returns new value:
88 * - kernel: -> 0x80
89 * - child: 0x80 -> 0x90
90 */
91
92 err = prog_attach(obj, cg_child, "cgroup/getsockopt/child");
93 if (err)
94 goto detach;
95
96 buf = 0x00;
97 optlen = 1;
98 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
99 if (err) {
100 log_err("Failed to call getsockopt(IP_TOS)");
101 goto detach;
102 }
103
104 if (buf != 0x90) {
105 log_err("Unexpected getsockopt 0x%x != 0x90", buf);
106 err = -1;
107 goto detach;
108 }
109
110 /* Attach parent program and make sure it returns new value:
111 * - kernel: -> 0x80
112 * - child: 0x80 -> 0x90
113 * - parent: 0x90 -> 0xA0
114 */
115
116 err = prog_attach(obj, cg_parent, "cgroup/getsockopt/parent");
117 if (err)
118 goto detach;
119
120 buf = 0x00;
121 optlen = 1;
122 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
123 if (err) {
124 log_err("Failed to call getsockopt(IP_TOS)");
125 goto detach;
126 }
127
128 if (buf != 0xA0) {
129 log_err("Unexpected getsockopt 0x%x != 0xA0", buf);
130 err = -1;
131 goto detach;
132 }
133
134 /* Setting unexpected initial sockopt should return EPERM:
135 * - kernel: -> 0x40
136 * - child: unexpected 0x40, EPERM
137 * - parent: unexpected 0x40, EPERM
138 */
139
140 buf = 0x40;
141 if (setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1) < 0) {
142 log_err("Failed to call setsockopt(IP_TOS)");
143 goto detach;
144 }
145
146 buf = 0x00;
147 optlen = 1;
148 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
149 if (!err) {
150 log_err("Unexpected success from getsockopt(IP_TOS)");
151 goto detach;
152 }
153
154 /* Detach child program and make sure we still get EPERM:
155 * - kernel: -> 0x40
156 * - parent: unexpected 0x40, EPERM
157 */
158
159 err = prog_detach(obj, cg_child, "cgroup/getsockopt/child");
160 if (err) {
161 log_err("Failed to detach child program");
162 goto detach;
163 }
164
165 buf = 0x00;
166 optlen = 1;
167 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
168 if (!err) {
169 log_err("Unexpected success from getsockopt(IP_TOS)");
170 goto detach;
171 }
172
173 /* Set initial value to the one the parent program expects:
174 * - kernel: -> 0x90
175 * - parent: 0x90 -> 0xA0
176 */
177
178 buf = 0x90;
179 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
180 if (err < 0) {
181 log_err("Failed to call setsockopt(IP_TOS)");
182 goto detach;
183 }
184
185 buf = 0x00;
186 optlen = 1;
187 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
188 if (err) {
189 log_err("Failed to call getsockopt(IP_TOS)");
190 goto detach;
191 }
192
193 if (buf != 0xA0) {
194 log_err("Unexpected getsockopt 0x%x != 0xA0", buf);
195 err = -1;
196 goto detach;
197 }
198
199 detach:
200 prog_detach(obj, cg_child, "cgroup/getsockopt/child");
201 prog_detach(obj, cg_parent, "cgroup/getsockopt/parent");
202
203 return err;
204 }
205
run_setsockopt_test(struct bpf_object * obj,int cg_parent,int cg_child,int sock_fd)206 static int run_setsockopt_test(struct bpf_object *obj, int cg_parent,
207 int cg_child, int sock_fd)
208 {
209 socklen_t optlen;
210 __u8 buf;
211 int err;
212
213 /* Set IP_TOS to the expected value (0x80). */
214
215 buf = 0x80;
216 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
217 if (err < 0) {
218 log_err("Failed to call setsockopt(IP_TOS)");
219 goto detach;
220 }
221
222 buf = 0x00;
223 optlen = 1;
224 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
225 if (err) {
226 log_err("Failed to call getsockopt(IP_TOS)");
227 goto detach;
228 }
229
230 if (buf != 0x80) {
231 log_err("Unexpected getsockopt 0x%x != 0x80 without BPF", buf);
232 err = -1;
233 goto detach;
234 }
235
236 /* Attach child program and make sure it adds 0x10. */
237
238 err = prog_attach(obj, cg_child, "cgroup/setsockopt");
239 if (err)
240 goto detach;
241
242 buf = 0x80;
243 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
244 if (err < 0) {
245 log_err("Failed to call setsockopt(IP_TOS)");
246 goto detach;
247 }
248
249 buf = 0x00;
250 optlen = 1;
251 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
252 if (err) {
253 log_err("Failed to call getsockopt(IP_TOS)");
254 goto detach;
255 }
256
257 if (buf != 0x80 + 0x10) {
258 log_err("Unexpected getsockopt 0x%x != 0x80 + 0x10", buf);
259 err = -1;
260 goto detach;
261 }
262
263 /* Attach parent program and make sure it adds another 0x10. */
264
265 err = prog_attach(obj, cg_parent, "cgroup/setsockopt");
266 if (err)
267 goto detach;
268
269 buf = 0x80;
270 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1);
271 if (err < 0) {
272 log_err("Failed to call setsockopt(IP_TOS)");
273 goto detach;
274 }
275
276 buf = 0x00;
277 optlen = 1;
278 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
279 if (err) {
280 log_err("Failed to call getsockopt(IP_TOS)");
281 goto detach;
282 }
283
284 if (buf != 0x80 + 2 * 0x10) {
285 log_err("Unexpected getsockopt 0x%x != 0x80 + 2 * 0x10", buf);
286 err = -1;
287 goto detach;
288 }
289
290 detach:
291 prog_detach(obj, cg_child, "cgroup/setsockopt");
292 prog_detach(obj, cg_parent, "cgroup/setsockopt");
293
294 return err;
295 }
296
test_sockopt_multi(void)297 void test_sockopt_multi(void)
298 {
299 struct bpf_prog_load_attr attr = {
300 .file = "./sockopt_multi.o",
301 };
302 int cg_parent = -1, cg_child = -1;
303 struct bpf_object *obj = NULL;
304 int sock_fd = -1;
305 int err = -1;
306 int ignored;
307
308 cg_parent = test__join_cgroup("/parent");
309 if (CHECK_FAIL(cg_parent < 0))
310 goto out;
311
312 cg_child = test__join_cgroup("/parent/child");
313 if (CHECK_FAIL(cg_child < 0))
314 goto out;
315
316 err = bpf_prog_load_xattr(&attr, &obj, &ignored);
317 if (CHECK_FAIL(err))
318 goto out;
319
320 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
321 if (CHECK_FAIL(sock_fd < 0))
322 goto out;
323
324 CHECK_FAIL(run_getsockopt_test(obj, cg_parent, cg_child, sock_fd));
325 CHECK_FAIL(run_setsockopt_test(obj, cg_parent, cg_child, sock_fd));
326
327 out:
328 close(sock_fd);
329 bpf_object__close(obj);
330 close(cg_child);
331 close(cg_parent);
332 }
333