550 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 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 0xdeadbeef16 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 that31 * 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 the67 * 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 then146 * one that sets the errno to EUNATCH. Assert that the get does not147 * 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 syscall192 * 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 errno240 * (legacy reject), and one that gets the errno. Assert that for241 * backward compatibility the syscall result in EPERM, and this242 * 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 reject286 * without setting errno, and then one that gets the exported errno.287 * Assert both the syscall and the helper's errno are unaffected by288 * the second prog (i.e. legacy rejects does not override the errno289 * 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 the339 * 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 this381 * 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 clears420 * ctx retval. Assert that the clearing ctx retval is synced to helper421 * 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_HOOK471};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}550