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