Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2
3/*
4 * Copyright 2021 Google LLC.
5 */
6
7#include <test_progs.h>
8#include <cgroup_helpers.h>
9#include <network_helpers.h>
10
11#include "cgroup_getset_retval_setsockopt.skel.h"
12#include "cgroup_getset_retval_getsockopt.skel.h"
13#include "cgroup_getset_retval_hooks.skel.h"
14
15#define SOL_CUSTOM 0xdeadbeef
16
17static int zero;
18
19static void test_setsockopt_set(int cgroup_fd, int sock_fd)
20{
21 struct cgroup_getset_retval_setsockopt *obj;
22 struct bpf_link *link_set_eunatch = NULL;
23
24 obj = cgroup_getset_retval_setsockopt__open_and_load();
25 if (!ASSERT_OK_PTR(obj, "skel-load"))
26 return;
27
28 obj->bss->page_size = sysconf(_SC_PAGESIZE);
29
30 /* Attach setsockopt that sets EUNATCH, assert that
31 * we actually get that error when we run setsockopt()
32 */
33 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
34 cgroup_fd);
35 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
36 goto close_bpf_object;
37
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;
43
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;
48
49close_bpf_object:
50 bpf_link__destroy(link_set_eunatch);
51
52 cgroup_getset_retval_setsockopt__destroy(obj);
53}
54
55static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd)
56{
57 struct cgroup_getset_retval_setsockopt *obj;
58 struct bpf_link *link_set_eunatch = NULL, *link_get_retval = NULL;
59
60 obj = cgroup_getset_retval_setsockopt__open_and_load();
61 if (!ASSERT_OK_PTR(obj, "skel-load"))
62 return;
63
64 obj->bss->page_size = sysconf(_SC_PAGESIZE);
65
66 /* Attach setsockopt that sets EUNATCH, and one that gets the
67 * previously set errno. Assert that we get the same errno back.
68 */
69 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
70 cgroup_fd);
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,
74 cgroup_fd);
75 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
76 goto close_bpf_object;
77
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;
83
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;
90
91close_bpf_object:
92 bpf_link__destroy(link_set_eunatch);
93 bpf_link__destroy(link_get_retval);
94
95 cgroup_getset_retval_setsockopt__destroy(obj);
96}
97
98static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd)
99{
100 struct cgroup_getset_retval_setsockopt *obj;
101 struct bpf_link *link_get_retval = NULL;
102
103 obj = cgroup_getset_retval_setsockopt__open_and_load();
104 if (!ASSERT_OK_PTR(obj, "skel-load"))
105 return;
106
107 obj->bss->page_size = sysconf(_SC_PAGESIZE);
108
109 /* Attach setsockopt that gets the previously set errno.
110 * Assert that, without anything setting one, we get 0.
111 */
112 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
113 cgroup_fd);
114 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
115 goto close_bpf_object;
116
117 if (!ASSERT_OK(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
118 &zero, sizeof(int)), "setsockopt"))
119 goto close_bpf_object;
120
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;
127
128close_bpf_object:
129 bpf_link__destroy(link_get_retval);
130
131 cgroup_getset_retval_setsockopt__destroy(obj);
132}
133
134static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd)
135{
136 struct cgroup_getset_retval_setsockopt *obj;
137 struct bpf_link *link_get_retval = NULL, *link_set_eunatch = NULL;
138
139 obj = cgroup_getset_retval_setsockopt__open_and_load();
140 if (!ASSERT_OK_PTR(obj, "skel-load"))
141 return;
142
143 obj->bss->page_size = sysconf(_SC_PAGESIZE);
144
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.
148 */
149 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
150 cgroup_fd);
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,
154 cgroup_fd);
155 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
156 goto close_bpf_object;
157
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;
163
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;
170
171close_bpf_object:
172 bpf_link__destroy(link_get_retval);
173 bpf_link__destroy(link_set_eunatch);
174
175 cgroup_getset_retval_setsockopt__destroy(obj);
176}
177
178static void test_setsockopt_override(int cgroup_fd, int sock_fd)
179{
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;
183
184 obj = cgroup_getset_retval_setsockopt__open_and_load();
185 if (!ASSERT_OK_PTR(obj, "skel-load"))
186 return;
187
188 obj->bss->page_size = sysconf(_SC_PAGESIZE);
189
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.
193 */
194 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
195 cgroup_fd);
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,
199 cgroup_fd);
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,
203 cgroup_fd);
204 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
205 goto close_bpf_object;
206
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;
212
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;
219
220close_bpf_object:
221 bpf_link__destroy(link_set_eunatch);
222 bpf_link__destroy(link_set_eisconn);
223 bpf_link__destroy(link_get_retval);
224
225 cgroup_getset_retval_setsockopt__destroy(obj);
226}
227
228static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd)
229{
230 struct cgroup_getset_retval_setsockopt *obj;
231 struct bpf_link *link_legacy_eperm = NULL, *link_get_retval = NULL;
232
233 obj = cgroup_getset_retval_setsockopt__open_and_load();
234 if (!ASSERT_OK_PTR(obj, "skel-load"))
235 return;
236
237 obj->bss->page_size = sysconf(_SC_PAGESIZE);
238
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.
243 */
244 link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
245 cgroup_fd);
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,
249 cgroup_fd);
250 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
251 goto close_bpf_object;
252
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;
258
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;
265
266close_bpf_object:
267 bpf_link__destroy(link_legacy_eperm);
268 bpf_link__destroy(link_get_retval);
269
270 cgroup_getset_retval_setsockopt__destroy(obj);
271}
272
273static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd)
274{
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;
278
279 obj = cgroup_getset_retval_setsockopt__open_and_load();
280 if (!ASSERT_OK_PTR(obj, "skel-load"))
281 return;
282
283 obj->bss->page_size = sysconf(_SC_PAGESIZE);
284
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
289 * to EPERM).
290 */
291 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
292 cgroup_fd);
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,
296 cgroup_fd);
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,
300 cgroup_fd);
301 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
302 goto close_bpf_object;
303
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;
309
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;
316
317close_bpf_object:
318 bpf_link__destroy(link_set_eunatch);
319 bpf_link__destroy(link_legacy_eperm);
320 bpf_link__destroy(link_get_retval);
321
322 cgroup_getset_retval_setsockopt__destroy(obj);
323}
324
325static void test_getsockopt_get(int cgroup_fd, int sock_fd)
326{
327 struct cgroup_getset_retval_getsockopt *obj;
328 struct bpf_link *link_get_retval = NULL;
329 int buf;
330 socklen_t optlen = sizeof(buf);
331
332 obj = cgroup_getset_retval_getsockopt__open_and_load();
333 if (!ASSERT_OK_PTR(obj, "skel-load"))
334 return;
335
336 obj->bss->page_size = sysconf(_SC_PAGESIZE);
337
338 /* Attach getsockopt that gets previously set errno. Assert that the
339 * error from kernel is in both ctx_retval_value and retval_value.
340 */
341 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
342 cgroup_fd);
343 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
344 goto close_bpf_object;
345
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;
351
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;
360
361close_bpf_object:
362 bpf_link__destroy(link_get_retval);
363
364 cgroup_getset_retval_getsockopt__destroy(obj);
365}
366
367static void test_getsockopt_override(int cgroup_fd, int sock_fd)
368{
369 struct cgroup_getset_retval_getsockopt *obj;
370 struct bpf_link *link_set_eisconn = NULL;
371 int buf;
372 socklen_t optlen = sizeof(buf);
373
374 obj = cgroup_getset_retval_getsockopt__open_and_load();
375 if (!ASSERT_OK_PTR(obj, "skel-load"))
376 return;
377
378 obj->bss->page_size = sysconf(_SC_PAGESIZE);
379
380 /* Attach getsockopt that sets retval to -EISCONN. Assert that this
381 * overrides the value from kernel.
382 */
383 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
384 cgroup_fd);
385 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
386 goto close_bpf_object;
387
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;
393
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;
398
399close_bpf_object:
400 bpf_link__destroy(link_set_eisconn);
401
402 cgroup_getset_retval_getsockopt__destroy(obj);
403}
404
405static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd)
406{
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;
410 int buf;
411 socklen_t optlen = sizeof(buf);
412
413 obj = cgroup_getset_retval_getsockopt__open_and_load();
414 if (!ASSERT_OK_PTR(obj, "skel-load"))
415 return;
416
417 obj->bss->page_size = sysconf(_SC_PAGESIZE);
418
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..
422 */
423 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
424 cgroup_fd);
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,
428 cgroup_fd);
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,
432 cgroup_fd);
433 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
434 goto close_bpf_object;
435
436 if (!ASSERT_OK(getsockopt(sock_fd, SOL_CUSTOM, 0,
437 &buf, &optlen), "getsockopt"))
438 goto close_bpf_object;
439
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;
448
449close_bpf_object:
450 bpf_link__destroy(link_set_eisconn);
451 bpf_link__destroy(link_clear_retval);
452 bpf_link__destroy(link_get_retval);
453
454 cgroup_getset_retval_getsockopt__destroy(obj);
455}
456
457struct exposed_hook {
458 const char *name;
459 int expected_err;
460} exposed_hooks[] = {
461
462#define BPF_RETVAL_HOOK(NAME, SECTION, CTX, EXPECTED_ERR) \
463 { \
464 .name = #NAME, \
465 .expected_err = EXPECTED_ERR, \
466 },
467
468#include "cgroup_getset_retval_hooks.h"
469
470#undef BPF_RETVAL_HOOK
471};
472
473static void test_exposed_hooks(int cgroup_fd, int sock_fd)
474{
475 struct cgroup_getset_retval_hooks *skel;
476 struct bpf_program *prog;
477 int err;
478 int i;
479
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"))
483 continue;
484
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"))
487 goto close_skel;
488
489 err = bpf_program__set_autoload(prog, true);
490 if (!ASSERT_OK(err, "bpf_program__set_autoload"))
491 goto close_skel;
492
493 err = cgroup_getset_retval_hooks__load(skel);
494 ASSERT_EQ(err, exposed_hooks[i].expected_err, "expected_err");
495
496close_skel:
497 cgroup_getset_retval_hooks__destroy(skel);
498 }
499}
500
501void test_cgroup_getset_retval(void)
502{
503 int cgroup_fd = -1;
504 int sock_fd = -1;
505
506 cgroup_fd = test__join_cgroup("/cgroup_getset_retval");
507 if (!ASSERT_GE(cgroup_fd, 0, "cg-create"))
508 goto close_fd;
509
510 sock_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);
511 if (!ASSERT_GE(sock_fd, 0, "start-server"))
512 goto close_fd;
513
514 if (test__start_subtest("setsockopt-set"))
515 test_setsockopt_set(cgroup_fd, sock_fd);
516
517 if (test__start_subtest("setsockopt-set_and_get"))
518 test_setsockopt_set_and_get(cgroup_fd, sock_fd);
519
520 if (test__start_subtest("setsockopt-default_zero"))
521 test_setsockopt_default_zero(cgroup_fd, sock_fd);
522
523 if (test__start_subtest("setsockopt-default_zero_and_set"))
524 test_setsockopt_default_zero_and_set(cgroup_fd, sock_fd);
525
526 if (test__start_subtest("setsockopt-override"))
527 test_setsockopt_override(cgroup_fd, sock_fd);
528
529 if (test__start_subtest("setsockopt-legacy_eperm"))
530 test_setsockopt_legacy_eperm(cgroup_fd, sock_fd);
531
532 if (test__start_subtest("setsockopt-legacy_no_override"))
533 test_setsockopt_legacy_no_override(cgroup_fd, sock_fd);
534
535 if (test__start_subtest("getsockopt-get"))
536 test_getsockopt_get(cgroup_fd, sock_fd);
537
538 if (test__start_subtest("getsockopt-override"))
539 test_getsockopt_override(cgroup_fd, sock_fd);
540
541 if (test__start_subtest("getsockopt-retval_sync"))
542 test_getsockopt_retval_sync(cgroup_fd, sock_fd);
543
544 if (test__start_subtest("exposed_hooks"))
545 test_exposed_hooks(cgroup_fd, sock_fd);
546
547close_fd:
548 close(cgroup_fd);
549}
1// SPDX-License-Identifier: GPL-2.0-only
2
3/*
4 * Copyright 2021 Google LLC.
5 */
6
7#include <test_progs.h>
8#include <cgroup_helpers.h>
9#include <network_helpers.h>
10
11#include "cgroup_getset_retval_setsockopt.skel.h"
12#include "cgroup_getset_retval_getsockopt.skel.h"
13#include "cgroup_getset_retval_hooks.skel.h"
14
15#define SOL_CUSTOM 0xdeadbeef
16
17static int zero;
18
19static void test_setsockopt_set(int cgroup_fd, int sock_fd)
20{
21 struct cgroup_getset_retval_setsockopt *obj;
22 struct bpf_link *link_set_eunatch = NULL;
23
24 obj = cgroup_getset_retval_setsockopt__open_and_load();
25 if (!ASSERT_OK_PTR(obj, "skel-load"))
26 return;
27
28 /* Attach setsockopt that sets EUNATCH, assert that
29 * we actually get that error when we run setsockopt()
30 */
31 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
32 cgroup_fd);
33 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
34 goto close_bpf_object;
35
36 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
37 &zero, sizeof(int)), "setsockopt"))
38 goto close_bpf_object;
39 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
40 goto close_bpf_object;
41
42 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
43 goto close_bpf_object;
44 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
45 goto close_bpf_object;
46
47close_bpf_object:
48 bpf_link__destroy(link_set_eunatch);
49
50 cgroup_getset_retval_setsockopt__destroy(obj);
51}
52
53static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd)
54{
55 struct cgroup_getset_retval_setsockopt *obj;
56 struct bpf_link *link_set_eunatch = NULL, *link_get_retval = NULL;
57
58 obj = cgroup_getset_retval_setsockopt__open_and_load();
59 if (!ASSERT_OK_PTR(obj, "skel-load"))
60 return;
61
62 /* Attach setsockopt that sets EUNATCH, and one that gets the
63 * previously set errno. Assert that we get the same errno back.
64 */
65 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
66 cgroup_fd);
67 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
68 goto close_bpf_object;
69 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
70 cgroup_fd);
71 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
72 goto close_bpf_object;
73
74 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
75 &zero, sizeof(int)), "setsockopt"))
76 goto close_bpf_object;
77 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
78 goto close_bpf_object;
79
80 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
81 goto close_bpf_object;
82 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
83 goto close_bpf_object;
84 if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value"))
85 goto close_bpf_object;
86
87close_bpf_object:
88 bpf_link__destroy(link_set_eunatch);
89 bpf_link__destroy(link_get_retval);
90
91 cgroup_getset_retval_setsockopt__destroy(obj);
92}
93
94static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd)
95{
96 struct cgroup_getset_retval_setsockopt *obj;
97 struct bpf_link *link_get_retval = NULL;
98
99 obj = cgroup_getset_retval_setsockopt__open_and_load();
100 if (!ASSERT_OK_PTR(obj, "skel-load"))
101 return;
102
103 /* Attach setsockopt that gets the previously set errno.
104 * Assert that, without anything setting one, we get 0.
105 */
106 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
107 cgroup_fd);
108 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
109 goto close_bpf_object;
110
111 if (!ASSERT_OK(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
112 &zero, sizeof(int)), "setsockopt"))
113 goto close_bpf_object;
114
115 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
116 goto close_bpf_object;
117 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
118 goto close_bpf_object;
119 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
120 goto close_bpf_object;
121
122close_bpf_object:
123 bpf_link__destroy(link_get_retval);
124
125 cgroup_getset_retval_setsockopt__destroy(obj);
126}
127
128static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd)
129{
130 struct cgroup_getset_retval_setsockopt *obj;
131 struct bpf_link *link_get_retval = NULL, *link_set_eunatch = NULL;
132
133 obj = cgroup_getset_retval_setsockopt__open_and_load();
134 if (!ASSERT_OK_PTR(obj, "skel-load"))
135 return;
136
137 /* Attach setsockopt that gets the previously set errno, and then
138 * one that sets the errno to EUNATCH. Assert that the get does not
139 * see EUNATCH set later, and does not prevent EUNATCH from being set.
140 */
141 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
142 cgroup_fd);
143 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
144 goto close_bpf_object;
145 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
146 cgroup_fd);
147 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
148 goto close_bpf_object;
149
150 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
151 &zero, sizeof(int)), "setsockopt"))
152 goto close_bpf_object;
153 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
154 goto close_bpf_object;
155
156 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
157 goto close_bpf_object;
158 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
159 goto close_bpf_object;
160 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
161 goto close_bpf_object;
162
163close_bpf_object:
164 bpf_link__destroy(link_get_retval);
165 bpf_link__destroy(link_set_eunatch);
166
167 cgroup_getset_retval_setsockopt__destroy(obj);
168}
169
170static void test_setsockopt_override(int cgroup_fd, int sock_fd)
171{
172 struct cgroup_getset_retval_setsockopt *obj;
173 struct bpf_link *link_set_eunatch = NULL, *link_set_eisconn = NULL;
174 struct bpf_link *link_get_retval = NULL;
175
176 obj = cgroup_getset_retval_setsockopt__open_and_load();
177 if (!ASSERT_OK_PTR(obj, "skel-load"))
178 return;
179
180 /* Attach setsockopt that sets EUNATCH, then one that sets EISCONN,
181 * and then one that gets the exported errno. Assert both the syscall
182 * and the helper sees the last set errno.
183 */
184 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
185 cgroup_fd);
186 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
187 goto close_bpf_object;
188 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
189 cgroup_fd);
190 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
191 goto close_bpf_object;
192 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
193 cgroup_fd);
194 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
195 goto close_bpf_object;
196
197 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
198 &zero, sizeof(int)), "setsockopt"))
199 goto close_bpf_object;
200 if (!ASSERT_EQ(errno, EISCONN, "setsockopt-errno"))
201 goto close_bpf_object;
202
203 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
204 goto close_bpf_object;
205 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
206 goto close_bpf_object;
207 if (!ASSERT_EQ(obj->bss->retval_value, -EISCONN, "retval_value"))
208 goto close_bpf_object;
209
210close_bpf_object:
211 bpf_link__destroy(link_set_eunatch);
212 bpf_link__destroy(link_set_eisconn);
213 bpf_link__destroy(link_get_retval);
214
215 cgroup_getset_retval_setsockopt__destroy(obj);
216}
217
218static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd)
219{
220 struct cgroup_getset_retval_setsockopt *obj;
221 struct bpf_link *link_legacy_eperm = NULL, *link_get_retval = NULL;
222
223 obj = cgroup_getset_retval_setsockopt__open_and_load();
224 if (!ASSERT_OK_PTR(obj, "skel-load"))
225 return;
226
227 /* Attach setsockopt that return a reject without setting errno
228 * (legacy reject), and one that gets the errno. Assert that for
229 * backward compatibility the syscall result in EPERM, and this
230 * is also visible to the helper.
231 */
232 link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
233 cgroup_fd);
234 if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm"))
235 goto close_bpf_object;
236 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
237 cgroup_fd);
238 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
239 goto close_bpf_object;
240
241 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
242 &zero, sizeof(int)), "setsockopt"))
243 goto close_bpf_object;
244 if (!ASSERT_EQ(errno, EPERM, "setsockopt-errno"))
245 goto close_bpf_object;
246
247 if (!ASSERT_EQ(obj->bss->invocations, 2, "invocations"))
248 goto close_bpf_object;
249 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
250 goto close_bpf_object;
251 if (!ASSERT_EQ(obj->bss->retval_value, -EPERM, "retval_value"))
252 goto close_bpf_object;
253
254close_bpf_object:
255 bpf_link__destroy(link_legacy_eperm);
256 bpf_link__destroy(link_get_retval);
257
258 cgroup_getset_retval_setsockopt__destroy(obj);
259}
260
261static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd)
262{
263 struct cgroup_getset_retval_setsockopt *obj;
264 struct bpf_link *link_set_eunatch = NULL, *link_legacy_eperm = NULL;
265 struct bpf_link *link_get_retval = NULL;
266
267 obj = cgroup_getset_retval_setsockopt__open_and_load();
268 if (!ASSERT_OK_PTR(obj, "skel-load"))
269 return;
270
271 /* Attach setsockopt that sets EUNATCH, then one that return a reject
272 * without setting errno, and then one that gets the exported errno.
273 * Assert both the syscall and the helper's errno are unaffected by
274 * the second prog (i.e. legacy rejects does not override the errno
275 * to EPERM).
276 */
277 link_set_eunatch = bpf_program__attach_cgroup(obj->progs.set_eunatch,
278 cgroup_fd);
279 if (!ASSERT_OK_PTR(link_set_eunatch, "cg-attach-set_eunatch"))
280 goto close_bpf_object;
281 link_legacy_eperm = bpf_program__attach_cgroup(obj->progs.legacy_eperm,
282 cgroup_fd);
283 if (!ASSERT_OK_PTR(link_legacy_eperm, "cg-attach-legacy_eperm"))
284 goto close_bpf_object;
285 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
286 cgroup_fd);
287 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
288 goto close_bpf_object;
289
290 if (!ASSERT_ERR(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
291 &zero, sizeof(int)), "setsockopt"))
292 goto close_bpf_object;
293 if (!ASSERT_EQ(errno, EUNATCH, "setsockopt-errno"))
294 goto close_bpf_object;
295
296 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
297 goto close_bpf_object;
298 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
299 goto close_bpf_object;
300 if (!ASSERT_EQ(obj->bss->retval_value, -EUNATCH, "retval_value"))
301 goto close_bpf_object;
302
303close_bpf_object:
304 bpf_link__destroy(link_set_eunatch);
305 bpf_link__destroy(link_legacy_eperm);
306 bpf_link__destroy(link_get_retval);
307
308 cgroup_getset_retval_setsockopt__destroy(obj);
309}
310
311static void test_getsockopt_get(int cgroup_fd, int sock_fd)
312{
313 struct cgroup_getset_retval_getsockopt *obj;
314 struct bpf_link *link_get_retval = NULL;
315 int buf;
316 socklen_t optlen = sizeof(buf);
317
318 obj = cgroup_getset_retval_getsockopt__open_and_load();
319 if (!ASSERT_OK_PTR(obj, "skel-load"))
320 return;
321
322 /* Attach getsockopt that gets previously set errno. Assert that the
323 * error from kernel is in both ctx_retval_value and retval_value.
324 */
325 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
326 cgroup_fd);
327 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
328 goto close_bpf_object;
329
330 if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0,
331 &buf, &optlen), "getsockopt"))
332 goto close_bpf_object;
333 if (!ASSERT_EQ(errno, EOPNOTSUPP, "getsockopt-errno"))
334 goto close_bpf_object;
335
336 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
337 goto close_bpf_object;
338 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
339 goto close_bpf_object;
340 if (!ASSERT_EQ(obj->bss->retval_value, -EOPNOTSUPP, "retval_value"))
341 goto close_bpf_object;
342 if (!ASSERT_EQ(obj->bss->ctx_retval_value, -EOPNOTSUPP, "ctx_retval_value"))
343 goto close_bpf_object;
344
345close_bpf_object:
346 bpf_link__destroy(link_get_retval);
347
348 cgroup_getset_retval_getsockopt__destroy(obj);
349}
350
351static void test_getsockopt_override(int cgroup_fd, int sock_fd)
352{
353 struct cgroup_getset_retval_getsockopt *obj;
354 struct bpf_link *link_set_eisconn = NULL;
355 int buf;
356 socklen_t optlen = sizeof(buf);
357
358 obj = cgroup_getset_retval_getsockopt__open_and_load();
359 if (!ASSERT_OK_PTR(obj, "skel-load"))
360 return;
361
362 /* Attach getsockopt that sets retval to -EISCONN. Assert that this
363 * overrides the value from kernel.
364 */
365 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
366 cgroup_fd);
367 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
368 goto close_bpf_object;
369
370 if (!ASSERT_ERR(getsockopt(sock_fd, SOL_CUSTOM, 0,
371 &buf, &optlen), "getsockopt"))
372 goto close_bpf_object;
373 if (!ASSERT_EQ(errno, EISCONN, "getsockopt-errno"))
374 goto close_bpf_object;
375
376 if (!ASSERT_EQ(obj->bss->invocations, 1, "invocations"))
377 goto close_bpf_object;
378 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
379 goto close_bpf_object;
380
381close_bpf_object:
382 bpf_link__destroy(link_set_eisconn);
383
384 cgroup_getset_retval_getsockopt__destroy(obj);
385}
386
387static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd)
388{
389 struct cgroup_getset_retval_getsockopt *obj;
390 struct bpf_link *link_set_eisconn = NULL, *link_clear_retval = NULL;
391 struct bpf_link *link_get_retval = NULL;
392 int buf;
393 socklen_t optlen = sizeof(buf);
394
395 obj = cgroup_getset_retval_getsockopt__open_and_load();
396 if (!ASSERT_OK_PTR(obj, "skel-load"))
397 return;
398
399 /* Attach getsockopt that sets retval to -EISCONN, and one that clears
400 * ctx retval. Assert that the clearing ctx retval is synced to helper
401 * and clears any errors both from kernel and BPF..
402 */
403 link_set_eisconn = bpf_program__attach_cgroup(obj->progs.set_eisconn,
404 cgroup_fd);
405 if (!ASSERT_OK_PTR(link_set_eisconn, "cg-attach-set_eisconn"))
406 goto close_bpf_object;
407 link_clear_retval = bpf_program__attach_cgroup(obj->progs.clear_retval,
408 cgroup_fd);
409 if (!ASSERT_OK_PTR(link_clear_retval, "cg-attach-clear_retval"))
410 goto close_bpf_object;
411 link_get_retval = bpf_program__attach_cgroup(obj->progs.get_retval,
412 cgroup_fd);
413 if (!ASSERT_OK_PTR(link_get_retval, "cg-attach-get_retval"))
414 goto close_bpf_object;
415
416 if (!ASSERT_OK(getsockopt(sock_fd, SOL_CUSTOM, 0,
417 &buf, &optlen), "getsockopt"))
418 goto close_bpf_object;
419
420 if (!ASSERT_EQ(obj->bss->invocations, 3, "invocations"))
421 goto close_bpf_object;
422 if (!ASSERT_FALSE(obj->bss->assertion_error, "assertion_error"))
423 goto close_bpf_object;
424 if (!ASSERT_EQ(obj->bss->retval_value, 0, "retval_value"))
425 goto close_bpf_object;
426 if (!ASSERT_EQ(obj->bss->ctx_retval_value, 0, "ctx_retval_value"))
427 goto close_bpf_object;
428
429close_bpf_object:
430 bpf_link__destroy(link_set_eisconn);
431 bpf_link__destroy(link_clear_retval);
432 bpf_link__destroy(link_get_retval);
433
434 cgroup_getset_retval_getsockopt__destroy(obj);
435}
436
437struct exposed_hook {
438 const char *name;
439 int expected_err;
440} exposed_hooks[] = {
441
442#define BPF_RETVAL_HOOK(NAME, SECTION, CTX, EXPECTED_ERR) \
443 { \
444 .name = #NAME, \
445 .expected_err = EXPECTED_ERR, \
446 },
447
448#include "cgroup_getset_retval_hooks.h"
449
450#undef BPF_RETVAL_HOOK
451};
452
453static void test_exposed_hooks(int cgroup_fd, int sock_fd)
454{
455 struct cgroup_getset_retval_hooks *skel;
456 struct bpf_program *prog;
457 int err;
458 int i;
459
460 for (i = 0; i < ARRAY_SIZE(exposed_hooks); i++) {
461 skel = cgroup_getset_retval_hooks__open();
462 if (!ASSERT_OK_PTR(skel, "cgroup_getset_retval_hooks__open"))
463 continue;
464
465 prog = bpf_object__find_program_by_name(skel->obj, exposed_hooks[i].name);
466 if (!ASSERT_NEQ(prog, NULL, "bpf_object__find_program_by_name"))
467 goto close_skel;
468
469 err = bpf_program__set_autoload(prog, true);
470 if (!ASSERT_OK(err, "bpf_program__set_autoload"))
471 goto close_skel;
472
473 err = cgroup_getset_retval_hooks__load(skel);
474 ASSERT_EQ(err, exposed_hooks[i].expected_err, "expected_err");
475
476close_skel:
477 cgroup_getset_retval_hooks__destroy(skel);
478 }
479}
480
481void test_cgroup_getset_retval(void)
482{
483 int cgroup_fd = -1;
484 int sock_fd = -1;
485
486 cgroup_fd = test__join_cgroup("/cgroup_getset_retval");
487 if (!ASSERT_GE(cgroup_fd, 0, "cg-create"))
488 goto close_fd;
489
490 sock_fd = start_server(AF_INET, SOCK_DGRAM, NULL, 0, 0);
491 if (!ASSERT_GE(sock_fd, 0, "start-server"))
492 goto close_fd;
493
494 if (test__start_subtest("setsockopt-set"))
495 test_setsockopt_set(cgroup_fd, sock_fd);
496
497 if (test__start_subtest("setsockopt-set_and_get"))
498 test_setsockopt_set_and_get(cgroup_fd, sock_fd);
499
500 if (test__start_subtest("setsockopt-default_zero"))
501 test_setsockopt_default_zero(cgroup_fd, sock_fd);
502
503 if (test__start_subtest("setsockopt-default_zero_and_set"))
504 test_setsockopt_default_zero_and_set(cgroup_fd, sock_fd);
505
506 if (test__start_subtest("setsockopt-override"))
507 test_setsockopt_override(cgroup_fd, sock_fd);
508
509 if (test__start_subtest("setsockopt-legacy_eperm"))
510 test_setsockopt_legacy_eperm(cgroup_fd, sock_fd);
511
512 if (test__start_subtest("setsockopt-legacy_no_override"))
513 test_setsockopt_legacy_no_override(cgroup_fd, sock_fd);
514
515 if (test__start_subtest("getsockopt-get"))
516 test_getsockopt_get(cgroup_fd, sock_fd);
517
518 if (test__start_subtest("getsockopt-override"))
519 test_getsockopt_override(cgroup_fd, sock_fd);
520
521 if (test__start_subtest("getsockopt-retval_sync"))
522 test_getsockopt_retval_sync(cgroup_fd, sock_fd);
523
524 if (test__start_subtest("exposed_hooks"))
525 test_exposed_hooks(cgroup_fd, sock_fd);
526
527close_fd:
528 close(cgroup_fd);
529}