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 17 static int zero; 18 19 static 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 47 close_bpf_object: 48 bpf_link__destroy(link_set_eunatch); 49 50 cgroup_getset_retval_setsockopt__destroy(obj); 51 } 52 53 static 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 87 close_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 94 static 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 122 close_bpf_object: 123 bpf_link__destroy(link_get_retval); 124 125 cgroup_getset_retval_setsockopt__destroy(obj); 126 } 127 128 static 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 163 close_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 170 static 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 210 close_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 218 static 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 254 close_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 261 static 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 303 close_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 311 static 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 345 close_bpf_object: 346 bpf_link__destroy(link_get_retval); 347 348 cgroup_getset_retval_getsockopt__destroy(obj); 349 } 350 351 static 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 381 close_bpf_object: 382 bpf_link__destroy(link_set_eisconn); 383 384 cgroup_getset_retval_getsockopt__destroy(obj); 385 } 386 387 static 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 429 close_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 437 struct 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 453 static 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 476 close_skel: 477 cgroup_getset_retval_hooks__destroy(skel); 478 } 479 } 480 481 void 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 527 close_fd: 528 close(cgroup_fd); 529 } 530