1 // SPDX-License-Identifier: GPL-2.0-only
4 * Copyright 2021 Google LLC.
7 #include <test_progs.h>
8 #include <cgroup_helpers.h>
9 #include <network_helpers.h>
11 #include "cgroup_getset_retval_setsockopt.skel.h"
12 #include "cgroup_getset_retval_getsockopt.skel.h"
13 #include "cgroup_getset_retval_hooks.skel.h"
15 #define SOL_CUSTOM 0xdeadbeef
19 static void test_setsockopt_set(int cgroup_fd, int sock_fd)
21 struct cgroup_getset_retval_setsockopt *obj;
22 struct bpf_link *link_set_eunatch = NULL;
24 obj = cgroup_getset_retval_setsockopt__open_and_load();
25 if (!ASSERT_OK_PTR(obj, "skel-load"))
28 obj->bss->page_size = sysconf(_SC_PAGESIZE);
30 /* Attach setsockopt that sets EUNATCH, assert that
31 * we actually get that error when we run setsockopt()
33 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
35 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
36 goto close_bpf_object;
38 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
39 &zero, sizeof(int)), "setsockopt"))
40 goto close_bpf_object;
41 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
42 goto close_bpf_object;
44 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
45 goto close_bpf_object;
46 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
47 goto close_bpf_object;
50 bpf_link__destroy(link_set_eunatch);
52 cgroup_getset_retval_setsockopt__destroy(obj);
55 static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd)
57 struct cgroup_getset_retval_setsockopt *obj;
58 struct bpf_link *link_set_eunatch = NULL, *link_get_retval = NULL;
60 obj = cgroup_getset_retval_setsockopt__open_and_load();
61 if (!ASSERT_OK_PTR(obj, "skel-load"))
64 obj->bss->page_size = sysconf(_SC_PAGESIZE);
66 /* Attach setsockopt that sets EUNATCH, and one that gets the
67 * previously set errno. Assert that we get the same errno back.
69 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
71 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
72 goto close_bpf_object;
73 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
75 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
76 goto close_bpf_object;
78 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
79 &zero, sizeof(int)), "setsockopt"))
80 goto close_bpf_object;
81 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
82 goto close_bpf_object;
84 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
85 goto close_bpf_object;
86 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
87 goto close_bpf_object;
88 if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value"))
89 goto close_bpf_object;
92 bpf_link__destroy(link_set_eunatch);
93 bpf_link__destroy(link_get_retval);
95 cgroup_getset_retval_setsockopt__destroy(obj);
98 static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd)
100 struct cgroup_getset_retval_setsockopt *obj;
101 struct bpf_link *link_get_retval = NULL;
103 obj = cgroup_getset_retval_setsockopt__open_and_load();
104 if (!ASSERT_OK_PTR(obj, "skel-load"))
107 obj->bss->page_size = sysconf(_SC_PAGESIZE);
109 /* Attach setsockopt that gets the previously set errno.
110 * Assert that, without anything setting one, we get 0.
112 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
114 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
115 goto close_bpf_object;
117 if (!ASSERT_OK(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
118 &zero, sizeof(int)), "setsockopt"))
119 goto close_bpf_object;
121 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
122 goto close_bpf_object;
123 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
124 goto close_bpf_object;
125 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
126 goto close_bpf_object;
129 bpf_link__destroy(link_get_retval);
131 cgroup_getset_retval_setsockopt__destroy(obj);
134 static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd)
136 struct cgroup_getset_retval_setsockopt *obj;
137 struct bpf_link *link_get_retval = NULL, *link_set_eunatch = NULL;
139 obj = cgroup_getset_retval_setsockopt__open_and_load();
140 if (!ASSERT_OK_PTR(obj, "skel-load"))
143 obj->bss->page_size = sysconf(_SC_PAGESIZE);
145 /* Attach setsockopt that gets the previously set errno, and then
146 * one that sets the errno to EUNATCH. Assert that the get does not
147 * see EUNATCH set later, and does not prevent EUNATCH from being set.
149 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
151 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
152 goto close_bpf_object;
153 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
155 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
156 goto close_bpf_object;
158 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
159 &zero, sizeof(int)), "setsockopt"))
160 goto close_bpf_object;
161 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
162 goto close_bpf_object;
164 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
165 goto close_bpf_object;
166 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
167 goto close_bpf_object;
168 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
169 goto close_bpf_object;
172 bpf_link__destroy(link_get_retval);
173 bpf_link__destroy(link_set_eunatch);
175 cgroup_getset_retval_setsockopt__destroy(obj);
178 static void test_setsockopt_override(int cgroup_fd, int sock_fd)
180 struct cgroup_getset_retval_setsockopt *obj;
181 struct bpf_link *link_set_eunatch = NULL, *link_set_eisconn = NULL;
182 struct bpf_link *link_get_retval = NULL;
184 obj = cgroup_getset_retval_setsockopt__open_and_load();
185 if (!ASSERT_OK_PTR(obj, "skel-load"))
188 obj->bss->page_size = sysconf(_SC_PAGESIZE);
190 /* Attach setsockopt that sets EUNATCH, then one that sets EISCONN,
191 * and then one that gets the exported errno. Assert both the syscall
192 * and the helper sees the last set errno.
194 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
196 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
197 goto close_bpf_object;
198 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
200 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
201 goto close_bpf_object;
202 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
204 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
205 goto close_bpf_object;
207 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
208 &zero, sizeof(int)), "setsockopt"))
209 goto close_bpf_object;
210 if (!ASSERT_EQ(errno, EISCONN, "setsockopt-errno"))
211 goto close_bpf_object;
213 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
214 goto close_bpf_object;
215 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
216 goto close_bpf_object;
217 if (!ASSERT_EQ(obj->bss->retval_value, -EISCONN, "retval_value"))
218 goto close_bpf_object;
221 bpf_link__destroy(link_set_eunatch);
222 bpf_link__destroy(link_set_eisconn);
223 bpf_link__destroy(link_get_retval);
225 cgroup_getset_retval_setsockopt__destroy(obj);
228 static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd)
230 struct cgroup_getset_retval_setsockopt *obj;
231 struct bpf_link *link_legacy_eperm = NULL, *link_get_retval = NULL;
233 obj = cgroup_getset_retval_setsockopt__open_and_load();
234 if (!ASSERT_OK_PTR(obj, "skel-load"))
237 obj->bss->page_size = sysconf(_SC_PAGESIZE);
239 /* Attach setsockopt that return a reject without setting errno
240 * (legacy reject), and one that gets the errno. Assert that for
241 * backward compatibility the syscall result in EPERM, and this
242 * is also visible to the helper.
244 link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
246 if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm"))
247 goto close_bpf_object;
248 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
250 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
251 goto close_bpf_object;
253 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
254 &zero, sizeof(int)), "setsockopt"))
255 goto close_bpf_object;
256 if (!ASSERT_EQ(errno, EPERM, "setsockopt-errno"))
257 goto close_bpf_object;
259 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
260 goto close_bpf_object;
261 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
262 goto close_bpf_object;
263 if (!ASSERT_EQ(obj->bss->retval_value, -EPERM, "retval_value"))
264 goto close_bpf_object;
267 bpf_link__destroy(link_legacy_eperm);
268 bpf_link__destroy(link_get_retval);
270 cgroup_getset_retval_setsockopt__destroy(obj);
273 static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd)
275 struct cgroup_getset_retval_setsockopt *obj;
276 struct bpf_link *link_set_eunatch = NULL, *link_legacy_eperm = NULL;
277 struct bpf_link *link_get_retval = NULL;
279 obj = cgroup_getset_retval_setsockopt__open_and_load();
280 if (!ASSERT_OK_PTR(obj, "skel-load"))
283 obj->bss->page_size = sysconf(_SC_PAGESIZE);
285 /* Attach setsockopt that sets EUNATCH, then one that return a reject
286 * without setting errno, and then one that gets the exported errno.
287 * Assert both the syscall and the helper's errno are unaffected by
288 * the second prog (i.e. legacy rejects does not override the errno
291 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
293 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
294 goto close_bpf_object;
295 link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
297 if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm"))
298 goto close_bpf_object;
299 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
301 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
302 goto close_bpf_object;
304 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
305 &zero, sizeof(int)), "setsockopt"))
306 goto close_bpf_object;
307 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
308 goto close_bpf_object;
310 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
311 goto close_bpf_object;
312 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
313 goto close_bpf_object;
314 if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value"))
315 goto close_bpf_object;
318 bpf_link__destroy(link_set_eunatch);
319 bpf_link__destroy(link_legacy_eperm);
320 bpf_link__destroy(link_get_retval);
322 cgroup_getset_retval_setsockopt__destroy(obj);
325 static void test_getsockopt_get(int cgroup_fd, int sock_fd)
327 struct cgroup_getset_retval_getsockopt *obj;
328 struct bpf_link *link_get_retval = NULL;
330 socklen_t optlen = sizeof(buf);
332 obj = cgroup_getset_retval_getsockopt__open_and_load();
333 if (!ASSERT_OK_PTR(obj, "skel-load"))
336 obj->bss->page_size = sysconf(_SC_PAGESIZE);
338 /* Attach getsockopt that gets previously set errno. Assert that the
339 * error from kernel is in both ctx_retval_value and retval_value.
341 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
343 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
344 goto close_bpf_object;
346 if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0,
347 &buf, &optlen), "getsockopt"))
348 goto close_bpf_object;
349 if (!ASSERT_EQ(errno, EOPNOTSUPP, "getsockopt-errno"))
350 goto close_bpf_object;
352 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
353 goto close_bpf_object;
354 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
355 goto close_bpf_object;
356 if (!ASSERT_EQ(obj->bss->retval_value, -EOPNOTSUPP, "retval_value"))
357 goto close_bpf_object;
358 if (!ASSERT_EQ(obj->bss->ctx_retval_value, -EOPNOTSUPP, "ctx_retval_value"))
359 goto close_bpf_object;
362 bpf_link__destroy(link_get_retval);
364 cgroup_getset_retval_getsockopt__destroy(obj);
367 static void test_getsockopt_override(int cgroup_fd, int sock_fd)
369 struct cgroup_getset_retval_getsockopt *obj;
370 struct bpf_link *link_set_eisconn = NULL;
372 socklen_t optlen = sizeof(buf);
374 obj = cgroup_getset_retval_getsockopt__open_and_load();
375 if (!ASSERT_OK_PTR(obj, "skel-load"))
378 obj->bss->page_size = sysconf(_SC_PAGESIZE);
380 /* Attach getsockopt that sets retval to -EISCONN. Assert that this
381 * overrides the value from kernel.
383 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
385 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
386 goto close_bpf_object;
388 if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0,
389 &buf, &optlen), "getsockopt"))
390 goto close_bpf_object;
391 if (!ASSERT_EQ(errno, EISCONN, "getsockopt-errno"))
392 goto close_bpf_object;
394 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
395 goto close_bpf_object;
396 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
397 goto close_bpf_object;
400 bpf_link__destroy(link_set_eisconn);
402 cgroup_getset_retval_getsockopt__destroy(obj);
405 static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd)
407 struct cgroup_getset_retval_getsockopt *obj;
408 struct bpf_link *link_set_eisconn = NULL, *link_clear_retval = NULL;
409 struct bpf_link *link_get_retval = NULL;
411 socklen_t optlen = sizeof(buf);
413 obj = cgroup_getset_retval_getsockopt__open_and_load();
414 if (!ASSERT_OK_PTR(obj, "skel-load"))
417 obj->bss->page_size = sysconf(_SC_PAGESIZE);
419 /* Attach getsockopt that sets retval to -EISCONN, and one that clears
420 * ctx retval. Assert that the clearing ctx retval is synced to helper
421 * and clears any errors both from kernel and BPF..
423 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
425 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
426 goto close_bpf_object;
427 link_clear_retval = bpf_program__attach_cgroup(obj->progs.clear_retval,
429 if (!ASSERT_OK_PTR(link_clear_retval, "cg-attach-clear_retval"))
430 goto close_bpf_object;
431 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
433 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
434 goto close_bpf_object;
436 if (!ASSERT_OK(getsockopt(sock_fd, SOL_CUSTOM, 0,
437 &buf, &optlen), "getsockopt"))
438 goto close_bpf_object;
440 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
441 goto close_bpf_object;
442 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
443 goto close_bpf_object;
444 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
445 goto close_bpf_object;
446 if (!ASSERT_EQ(obj->bss->ctx_retval_value, 0, "ctx_retval_value"))
447 goto close_bpf_object;
450 bpf_link__destroy(link_set_eisconn);
451 bpf_link__destroy(link_clear_retval);
452 bpf_link__destroy(link_get_retval);
454 cgroup_getset_retval_getsockopt__destroy(obj);
457 struct exposed_hook {
460 } exposed_hooks[] = {
462 #define BPF_RETVAL_HOOK(NAME, SECTION, CTX, EXPECTED_ERR) \
465 .expected_err = EXPECTED_ERR, \
468 #include "cgroup_getset_retval_hooks.h"
470 #undef BPF_RETVAL_HOOK
473 static void test_exposed_hooks(int cgroup_fd, int sock_fd)
475 struct cgroup_getset_retval_hooks *skel;
476 struct bpf_program *prog;
480 for (i = 0; i < ARRAY_SIZE(exposed_hooks); i++) {
481 skel = cgroup_getset_retval_hooks__open();
482 if (!ASSERT_OK_PTR(skel, "cgroup_getset_retval_hooks__open"))
485 prog = bpf_object__find_program_by_name(skel->obj, exposed_hooks[i].name);
486 if (!ASSERT_NEQ(prog, NULL, "bpf_object__find_program_by_name"))
489 err = bpf_program__set_autoload(prog, true);
490 if (!ASSERT_OK(err, "bpf_program__set_autoload"))
493 err = cgroup_getset_retval_hooks__load(skel);
494 ASSERT_EQ(err, exposed_hooks[i].expected_err, "expected_err");
497 cgroup_getset_retval_hooks__destroy(skel);
501 void test_cgroup_getset_retval(void)
506 cgroup_fd = test__join_cgroup("/cgroup_getset_retval");
507 if (!ASSERT_GE(cgroup_fd, 0, "cg-create"))
510 sock_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);
511 if (!ASSERT_GE(sock_fd, 0, "start-server"))
514 if (test__start_subtest("setsockopt-set"))
515 test_setsockopt_set(cgroup_fd, sock_fd);
517 if (test__start_subtest("setsockopt-set_and_get"))
518 test_setsockopt_set_and_get(cgroup_fd, sock_fd);
520 if (test__start_subtest("setsockopt-default_zero"))
521 test_setsockopt_default_zero(cgroup_fd, sock_fd);
523 if (test__start_subtest("setsockopt-default_zero_and_set"))
524 test_setsockopt_default_zero_and_set(cgroup_fd, sock_fd);
526 if (test__start_subtest("setsockopt-override"))
527 test_setsockopt_override(cgroup_fd, sock_fd);
529 if (test__start_subtest("setsockopt-legacy_eperm"))
530 test_setsockopt_legacy_eperm(cgroup_fd, sock_fd);
532 if (test__start_subtest("setsockopt-legacy_no_override"))
533 test_setsockopt_legacy_no_override(cgroup_fd, sock_fd);
535 if (test__start_subtest("getsockopt-get"))
536 test_getsockopt_get(cgroup_fd, sock_fd);
538 if (test__start_subtest("getsockopt-override"))
539 test_getsockopt_override(cgroup_fd, sock_fd);
541 if (test__start_subtest("getsockopt-retval_sync"))
542 test_getsockopt_retval_sync(cgroup_fd, sock_fd);
544 if (test__start_subtest("exposed_hooks"))
545 test_exposed_hooks(cgroup_fd, sock_fd);