Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add two selftests for mprog API based cgroup progs

Two tests are added:
- cgroup_mprog_opts, which mimics tc_opts.c ([1]). Both prog and link
attach are tested. Some negative tests are also included.
- cgroup_mprog_ordering, which actually runs the program with some mprog
API flags.

[1] https://github.com/torvalds/linux/blob/master/tools/testing/selftests/bpf/prog_tests/tc_opts.c

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20250606163156.2429955-1-yonghong.song@linux.dev

authored by

Yonghong Song and committed by
Andrii Nakryiko
e422d5f1 c1bb6865

+724
+617
tools/testing/selftests/bpf/prog_tests/cgroup_mprog_opts.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + #include <test_progs.h> 4 + #include "cgroup_helpers.h" 5 + #include "cgroup_mprog.skel.h" 6 + 7 + static void assert_mprog_count(int cg, int atype, int expected) 8 + { 9 + __u32 count = 0, attach_flags = 0; 10 + int err; 11 + 12 + err = bpf_prog_query(cg, atype, 0, &attach_flags, 13 + NULL, &count); 14 + ASSERT_EQ(count, expected, "count"); 15 + ASSERT_EQ(err, 0, "prog_query"); 16 + } 17 + 18 + static void test_prog_attach_detach(int atype) 19 + { 20 + LIBBPF_OPTS(bpf_prog_attach_opts, opta); 21 + LIBBPF_OPTS(bpf_prog_detach_opts, optd); 22 + LIBBPF_OPTS(bpf_prog_query_opts, optq); 23 + __u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4; 24 + struct cgroup_mprog *skel; 25 + __u32 prog_ids[10]; 26 + int cg, err; 27 + 28 + cg = test__join_cgroup("/prog_attach_detach"); 29 + if (!ASSERT_GE(cg, 0, "join_cgroup /prog_attach_detach")) 30 + return; 31 + 32 + skel = cgroup_mprog__open_and_load(); 33 + if (!ASSERT_OK_PTR(skel, "skel_load")) 34 + goto cleanup; 35 + 36 + fd1 = bpf_program__fd(skel->progs.getsockopt_1); 37 + fd2 = bpf_program__fd(skel->progs.getsockopt_2); 38 + fd3 = bpf_program__fd(skel->progs.getsockopt_3); 39 + fd4 = bpf_program__fd(skel->progs.getsockopt_4); 40 + 41 + id1 = id_from_prog_fd(fd1); 42 + id2 = id_from_prog_fd(fd2); 43 + id3 = id_from_prog_fd(fd3); 44 + id4 = id_from_prog_fd(fd4); 45 + 46 + assert_mprog_count(cg, atype, 0); 47 + 48 + LIBBPF_OPTS_RESET(opta, 49 + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, 50 + .expected_revision = 1, 51 + ); 52 + 53 + /* ordering: [fd1] */ 54 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 55 + if (!ASSERT_EQ(err, 0, "prog_attach")) 56 + goto cleanup; 57 + 58 + assert_mprog_count(cg, atype, 1); 59 + 60 + LIBBPF_OPTS_RESET(opta, 61 + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE, 62 + .expected_revision = 2, 63 + ); 64 + 65 + /* ordering: [fd2, fd1] */ 66 + err = bpf_prog_attach_opts(fd2, cg, atype, &opta); 67 + if (!ASSERT_EQ(err, 0, "prog_attach")) 68 + goto cleanup1; 69 + 70 + assert_mprog_count(cg, atype, 2); 71 + 72 + LIBBPF_OPTS_RESET(opta, 73 + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, 74 + .relative_fd = fd2, 75 + .expected_revision = 3, 76 + ); 77 + 78 + /* ordering: [fd2, fd3, fd1] */ 79 + err = bpf_prog_attach_opts(fd3, cg, atype, &opta); 80 + if (!ASSERT_EQ(err, 0, "prog_attach")) 81 + goto cleanup2; 82 + 83 + assert_mprog_count(cg, atype, 3); 84 + 85 + LIBBPF_OPTS_RESET(opta, 86 + .flags = BPF_F_ALLOW_MULTI, 87 + .expected_revision = 4, 88 + ); 89 + 90 + /* ordering: [fd2, fd3, fd1, fd4] */ 91 + err = bpf_prog_attach_opts(fd4, cg, atype, &opta); 92 + if (!ASSERT_EQ(err, 0, "prog_attach")) 93 + goto cleanup3; 94 + 95 + assert_mprog_count(cg, atype, 4); 96 + 97 + /* retrieve optq.prog_cnt */ 98 + err = bpf_prog_query_opts(cg, atype, &optq); 99 + if (!ASSERT_OK(err, "prog_query")) 100 + goto cleanup4; 101 + 102 + /* optq.prog_cnt will be used in below query */ 103 + memset(prog_ids, 0, sizeof(prog_ids)); 104 + optq.prog_ids = prog_ids; 105 + err = bpf_prog_query_opts(cg, atype, &optq); 106 + if (!ASSERT_OK(err, "prog_query")) 107 + goto cleanup4; 108 + 109 + ASSERT_EQ(optq.count, 4, "count"); 110 + ASSERT_EQ(optq.revision, 5, "revision"); 111 + ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]"); 112 + ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]"); 113 + ASSERT_EQ(optq.prog_ids[2], id1, "prog_ids[2]"); 114 + ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]"); 115 + ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); 116 + ASSERT_EQ(optq.link_ids, NULL, "link_ids"); 117 + 118 + cleanup4: 119 + optd.expected_revision = 5; 120 + err = bpf_prog_detach_opts(fd4, cg, atype, &optd); 121 + ASSERT_OK(err, "prog_detach"); 122 + assert_mprog_count(cg, atype, 3); 123 + 124 + cleanup3: 125 + LIBBPF_OPTS_RESET(optd); 126 + err = bpf_prog_detach_opts(fd3, cg, atype, &optd); 127 + ASSERT_OK(err, "prog_detach"); 128 + assert_mprog_count(cg, atype, 2); 129 + 130 + /* Check revision after two detach operations */ 131 + err = bpf_prog_query_opts(cg, atype, &optq); 132 + ASSERT_OK(err, "prog_query"); 133 + ASSERT_EQ(optq.revision, 7, "revision"); 134 + 135 + cleanup2: 136 + err = bpf_prog_detach_opts(fd2, cg, atype, &optd); 137 + ASSERT_OK(err, "prog_detach"); 138 + assert_mprog_count(cg, atype, 1); 139 + 140 + cleanup1: 141 + err = bpf_prog_detach_opts(fd1, cg, atype, &optd); 142 + ASSERT_OK(err, "prog_detach"); 143 + assert_mprog_count(cg, atype, 0); 144 + 145 + cleanup: 146 + cgroup_mprog__destroy(skel); 147 + close(cg); 148 + } 149 + 150 + static void test_link_attach_detach(int atype) 151 + { 152 + LIBBPF_OPTS(bpf_cgroup_opts, opta); 153 + LIBBPF_OPTS(bpf_cgroup_opts, optd); 154 + LIBBPF_OPTS(bpf_prog_query_opts, optq); 155 + struct bpf_link *link1, *link2, *link3, *link4; 156 + __u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4; 157 + struct cgroup_mprog *skel; 158 + __u32 prog_ids[10]; 159 + int cg, err; 160 + 161 + cg = test__join_cgroup("/link_attach_detach"); 162 + if (!ASSERT_GE(cg, 0, "join_cgroup /link_attach_detach")) 163 + return; 164 + 165 + skel = cgroup_mprog__open_and_load(); 166 + if (!ASSERT_OK_PTR(skel, "skel_load")) 167 + goto cleanup; 168 + 169 + fd1 = bpf_program__fd(skel->progs.getsockopt_1); 170 + fd2 = bpf_program__fd(skel->progs.getsockopt_2); 171 + fd3 = bpf_program__fd(skel->progs.getsockopt_3); 172 + fd4 = bpf_program__fd(skel->progs.getsockopt_4); 173 + 174 + id1 = id_from_prog_fd(fd1); 175 + id2 = id_from_prog_fd(fd2); 176 + id3 = id_from_prog_fd(fd3); 177 + id4 = id_from_prog_fd(fd4); 178 + 179 + assert_mprog_count(cg, atype, 0); 180 + 181 + LIBBPF_OPTS_RESET(opta, 182 + .expected_revision = 1, 183 + ); 184 + 185 + /* ordering: [fd1] */ 186 + link1 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_1, cg, &opta); 187 + if (!ASSERT_OK_PTR(link1, "link_attach")) 188 + goto cleanup; 189 + 190 + assert_mprog_count(cg, atype, 1); 191 + 192 + LIBBPF_OPTS_RESET(opta, 193 + .flags = BPF_F_BEFORE | BPF_F_LINK, 194 + .relative_id = id_from_link_fd(bpf_link__fd(link1)), 195 + .expected_revision = 2, 196 + ); 197 + 198 + /* ordering: [fd2, fd1] */ 199 + link2 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_2, cg, &opta); 200 + if (!ASSERT_OK_PTR(link2, "link_attach")) 201 + goto cleanup1; 202 + 203 + assert_mprog_count(cg, atype, 2); 204 + 205 + LIBBPF_OPTS_RESET(opta, 206 + .flags = BPF_F_AFTER | BPF_F_LINK, 207 + .relative_fd = bpf_link__fd(link2), 208 + .expected_revision = 3, 209 + ); 210 + 211 + /* ordering: [fd2, fd3, fd1] */ 212 + link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); 213 + if (!ASSERT_OK_PTR(link3, "link_attach")) 214 + goto cleanup2; 215 + 216 + assert_mprog_count(cg, atype, 3); 217 + 218 + LIBBPF_OPTS_RESET(opta, 219 + .expected_revision = 4, 220 + ); 221 + 222 + /* ordering: [fd2, fd3, fd1, fd4] */ 223 + link4 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_4, cg, &opta); 224 + if (!ASSERT_OK_PTR(link4, "link_attach")) 225 + goto cleanup3; 226 + 227 + assert_mprog_count(cg, atype, 4); 228 + 229 + /* retrieve optq.prog_cnt */ 230 + err = bpf_prog_query_opts(cg, atype, &optq); 231 + if (!ASSERT_OK(err, "prog_query")) 232 + goto cleanup4; 233 + 234 + /* optq.prog_cnt will be used in below query */ 235 + memset(prog_ids, 0, sizeof(prog_ids)); 236 + optq.prog_ids = prog_ids; 237 + err = bpf_prog_query_opts(cg, atype, &optq); 238 + if (!ASSERT_OK(err, "prog_query")) 239 + goto cleanup4; 240 + 241 + ASSERT_EQ(optq.count, 4, "count"); 242 + ASSERT_EQ(optq.revision, 5, "revision"); 243 + ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]"); 244 + ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]"); 245 + ASSERT_EQ(optq.prog_ids[2], id1, "prog_ids[2]"); 246 + ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]"); 247 + ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); 248 + ASSERT_EQ(optq.link_ids, NULL, "link_ids"); 249 + 250 + cleanup4: 251 + bpf_link__destroy(link4); 252 + assert_mprog_count(cg, atype, 3); 253 + 254 + cleanup3: 255 + bpf_link__destroy(link3); 256 + assert_mprog_count(cg, atype, 2); 257 + 258 + /* Check revision after two detach operations */ 259 + err = bpf_prog_query_opts(cg, atype, &optq); 260 + ASSERT_OK(err, "prog_query"); 261 + ASSERT_EQ(optq.revision, 7, "revision"); 262 + 263 + cleanup2: 264 + bpf_link__destroy(link2); 265 + assert_mprog_count(cg, atype, 1); 266 + 267 + cleanup1: 268 + bpf_link__destroy(link1); 269 + assert_mprog_count(cg, atype, 0); 270 + 271 + cleanup: 272 + cgroup_mprog__destroy(skel); 273 + close(cg); 274 + } 275 + 276 + static void test_preorder_prog_attach_detach(int atype) 277 + { 278 + LIBBPF_OPTS(bpf_prog_attach_opts, opta); 279 + LIBBPF_OPTS(bpf_prog_detach_opts, optd); 280 + __u32 fd1, fd2, fd3, fd4; 281 + struct cgroup_mprog *skel; 282 + int cg, err; 283 + 284 + cg = test__join_cgroup("/preorder_prog_attach_detach"); 285 + if (!ASSERT_GE(cg, 0, "join_cgroup /preorder_prog_attach_detach")) 286 + return; 287 + 288 + skel = cgroup_mprog__open_and_load(); 289 + if (!ASSERT_OK_PTR(skel, "skel_load")) 290 + goto cleanup; 291 + 292 + fd1 = bpf_program__fd(skel->progs.getsockopt_1); 293 + fd2 = bpf_program__fd(skel->progs.getsockopt_2); 294 + fd3 = bpf_program__fd(skel->progs.getsockopt_3); 295 + fd4 = bpf_program__fd(skel->progs.getsockopt_4); 296 + 297 + assert_mprog_count(cg, atype, 0); 298 + 299 + LIBBPF_OPTS_RESET(opta, 300 + .flags = BPF_F_ALLOW_MULTI, 301 + .expected_revision = 1, 302 + ); 303 + 304 + /* ordering: [fd1] */ 305 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 306 + if (!ASSERT_EQ(err, 0, "prog_attach")) 307 + goto cleanup; 308 + 309 + assert_mprog_count(cg, atype, 1); 310 + 311 + LIBBPF_OPTS_RESET(opta, 312 + .flags = BPF_F_ALLOW_MULTI | BPF_F_PREORDER, 313 + .expected_revision = 2, 314 + ); 315 + 316 + /* ordering: [fd1, fd2] */ 317 + err = bpf_prog_attach_opts(fd2, cg, atype, &opta); 318 + if (!ASSERT_EQ(err, 0, "prog_attach")) 319 + goto cleanup1; 320 + 321 + assert_mprog_count(cg, atype, 2); 322 + 323 + LIBBPF_OPTS_RESET(opta, 324 + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, 325 + .relative_fd = fd2, 326 + .expected_revision = 3, 327 + ); 328 + 329 + err = bpf_prog_attach_opts(fd3, cg, atype, &opta); 330 + if (!ASSERT_EQ(err, -EINVAL, "prog_attach")) 331 + goto cleanup2; 332 + 333 + assert_mprog_count(cg, atype, 2); 334 + 335 + LIBBPF_OPTS_RESET(opta, 336 + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER | BPF_F_PREORDER, 337 + .relative_fd = fd2, 338 + .expected_revision = 3, 339 + ); 340 + 341 + /* ordering: [fd1, fd2, fd3] */ 342 + err = bpf_prog_attach_opts(fd3, cg, atype, &opta); 343 + if (!ASSERT_EQ(err, 0, "prog_attach")) 344 + goto cleanup2; 345 + 346 + assert_mprog_count(cg, atype, 3); 347 + 348 + LIBBPF_OPTS_RESET(opta, 349 + .flags = BPF_F_ALLOW_MULTI, 350 + .expected_revision = 4, 351 + ); 352 + 353 + /* ordering: [fd2, fd3, fd1, fd4] */ 354 + err = bpf_prog_attach_opts(fd4, cg, atype, &opta); 355 + if (!ASSERT_EQ(err, 0, "prog_attach")) 356 + goto cleanup3; 357 + 358 + assert_mprog_count(cg, atype, 4); 359 + 360 + err = bpf_prog_detach_opts(fd4, cg, atype, &optd); 361 + ASSERT_OK(err, "prog_detach"); 362 + assert_mprog_count(cg, atype, 3); 363 + 364 + cleanup3: 365 + err = bpf_prog_detach_opts(fd3, cg, atype, &optd); 366 + ASSERT_OK(err, "prog_detach"); 367 + assert_mprog_count(cg, atype, 2); 368 + 369 + cleanup2: 370 + err = bpf_prog_detach_opts(fd2, cg, atype, &optd); 371 + ASSERT_OK(err, "prog_detach"); 372 + assert_mprog_count(cg, atype, 1); 373 + 374 + cleanup1: 375 + err = bpf_prog_detach_opts(fd1, cg, atype, &optd); 376 + ASSERT_OK(err, "prog_detach"); 377 + assert_mprog_count(cg, atype, 0); 378 + 379 + cleanup: 380 + cgroup_mprog__destroy(skel); 381 + close(cg); 382 + } 383 + 384 + static void test_preorder_link_attach_detach(int atype) 385 + { 386 + LIBBPF_OPTS(bpf_cgroup_opts, opta); 387 + struct bpf_link *link1, *link2, *link3, *link4; 388 + struct cgroup_mprog *skel; 389 + __u32 fd2; 390 + int cg; 391 + 392 + cg = test__join_cgroup("/preorder_link_attach_detach"); 393 + if (!ASSERT_GE(cg, 0, "join_cgroup /preorder_link_attach_detach")) 394 + return; 395 + 396 + skel = cgroup_mprog__open_and_load(); 397 + if (!ASSERT_OK_PTR(skel, "skel_load")) 398 + goto cleanup; 399 + 400 + fd2 = bpf_program__fd(skel->progs.getsockopt_2); 401 + 402 + assert_mprog_count(cg, atype, 0); 403 + 404 + LIBBPF_OPTS_RESET(opta, 405 + .expected_revision = 1, 406 + ); 407 + 408 + /* ordering: [fd1] */ 409 + link1 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_1, cg, &opta); 410 + if (!ASSERT_OK_PTR(link1, "link_attach")) 411 + goto cleanup; 412 + 413 + assert_mprog_count(cg, atype, 1); 414 + 415 + LIBBPF_OPTS_RESET(opta, 416 + .flags = BPF_F_PREORDER, 417 + .expected_revision = 2, 418 + ); 419 + 420 + /* ordering: [fd1, fd2] */ 421 + link2 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_2, cg, &opta); 422 + if (!ASSERT_OK_PTR(link2, "link_attach")) 423 + goto cleanup1; 424 + 425 + assert_mprog_count(cg, atype, 2); 426 + 427 + LIBBPF_OPTS_RESET(opta, 428 + .flags = BPF_F_AFTER, 429 + .relative_fd = fd2, 430 + .expected_revision = 3, 431 + ); 432 + 433 + link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); 434 + if (!ASSERT_ERR_PTR(link3, "link_attach")) 435 + goto cleanup2; 436 + 437 + assert_mprog_count(cg, atype, 2); 438 + 439 + LIBBPF_OPTS_RESET(opta, 440 + .flags = BPF_F_AFTER | BPF_F_PREORDER | BPF_F_LINK, 441 + .relative_fd = bpf_link__fd(link2), 442 + .expected_revision = 3, 443 + ); 444 + 445 + /* ordering: [fd1, fd2, fd3] */ 446 + link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); 447 + if (!ASSERT_OK_PTR(link3, "link_attach")) 448 + goto cleanup2; 449 + 450 + assert_mprog_count(cg, atype, 3); 451 + 452 + LIBBPF_OPTS_RESET(opta, 453 + .expected_revision = 4, 454 + ); 455 + 456 + /* ordering: [fd2, fd3, fd1, fd4] */ 457 + link4 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_4, cg, &opta); 458 + if (!ASSERT_OK_PTR(link4, "prog_attach")) 459 + goto cleanup3; 460 + 461 + assert_mprog_count(cg, atype, 4); 462 + 463 + bpf_link__destroy(link4); 464 + assert_mprog_count(cg, atype, 3); 465 + 466 + cleanup3: 467 + bpf_link__destroy(link3); 468 + assert_mprog_count(cg, atype, 2); 469 + 470 + cleanup2: 471 + bpf_link__destroy(link2); 472 + assert_mprog_count(cg, atype, 1); 473 + 474 + cleanup1: 475 + bpf_link__destroy(link1); 476 + assert_mprog_count(cg, atype, 0); 477 + 478 + cleanup: 479 + cgroup_mprog__destroy(skel); 480 + close(cg); 481 + } 482 + 483 + static void test_invalid_attach_detach(int atype) 484 + { 485 + LIBBPF_OPTS(bpf_prog_attach_opts, opta); 486 + __u32 fd1, fd2, id2; 487 + struct cgroup_mprog *skel; 488 + int cg, err; 489 + 490 + cg = test__join_cgroup("/invalid_attach_detach"); 491 + if (!ASSERT_GE(cg, 0, "join_cgroup /invalid_attach_detach")) 492 + return; 493 + 494 + skel = cgroup_mprog__open_and_load(); 495 + if (!ASSERT_OK_PTR(skel, "skel_load")) 496 + goto cleanup; 497 + 498 + fd1 = bpf_program__fd(skel->progs.getsockopt_1); 499 + fd2 = bpf_program__fd(skel->progs.getsockopt_2); 500 + 501 + id2 = id_from_prog_fd(fd2); 502 + 503 + assert_mprog_count(cg, atype, 0); 504 + 505 + LIBBPF_OPTS_RESET(opta, 506 + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, 507 + .relative_id = id2, 508 + ); 509 + 510 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 511 + ASSERT_EQ(err, -EINVAL, "prog_attach"); 512 + assert_mprog_count(cg, atype, 0); 513 + 514 + LIBBPF_OPTS_RESET(opta, 515 + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_ID, 516 + ); 517 + 518 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 519 + ASSERT_EQ(err, -ENOENT, "prog_attach"); 520 + assert_mprog_count(cg, atype, 0); 521 + 522 + LIBBPF_OPTS_RESET(opta, 523 + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER | BPF_F_ID, 524 + ); 525 + 526 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 527 + ASSERT_EQ(err, -ENOENT, "prog_attach"); 528 + assert_mprog_count(cg, atype, 0); 529 + 530 + LIBBPF_OPTS_RESET(opta, 531 + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, 532 + .relative_id = id2, 533 + ); 534 + 535 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 536 + ASSERT_EQ(err, -EINVAL, "prog_attach"); 537 + assert_mprog_count(cg, atype, 0); 538 + 539 + LIBBPF_OPTS_RESET(opta, 540 + .flags = BPF_F_ALLOW_MULTI | BPF_F_LINK, 541 + .relative_id = id2, 542 + ); 543 + 544 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 545 + ASSERT_EQ(err, -EINVAL, "prog_attach"); 546 + assert_mprog_count(cg, atype, 0); 547 + 548 + LIBBPF_OPTS_RESET(opta, 549 + .flags = BPF_F_ALLOW_MULTI, 550 + .relative_id = id2, 551 + ); 552 + 553 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 554 + ASSERT_EQ(err, -EINVAL, "prog_attach"); 555 + assert_mprog_count(cg, atype, 0); 556 + 557 + LIBBPF_OPTS_RESET(opta, 558 + .flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE, 559 + .relative_fd = fd1, 560 + ); 561 + 562 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 563 + ASSERT_EQ(err, -ENOENT, "prog_attach"); 564 + assert_mprog_count(cg, atype, 0); 565 + 566 + LIBBPF_OPTS_RESET(opta, 567 + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, 568 + .relative_fd = fd1, 569 + ); 570 + 571 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 572 + ASSERT_EQ(err, -ENOENT, "prog_attach"); 573 + assert_mprog_count(cg, atype, 0); 574 + 575 + LIBBPF_OPTS_RESET(opta, 576 + .flags = BPF_F_ALLOW_MULTI, 577 + ); 578 + 579 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 580 + if (!ASSERT_EQ(err, 0, "prog_attach")) 581 + goto cleanup; 582 + assert_mprog_count(cg, atype, 1); 583 + 584 + LIBBPF_OPTS_RESET(opta, 585 + .flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, 586 + ); 587 + 588 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 589 + ASSERT_EQ(err, -EINVAL, "prog_attach"); 590 + assert_mprog_count(cg, atype, 1); 591 + 592 + LIBBPF_OPTS_RESET(opta, 593 + .flags = BPF_F_ALLOW_MULTI | BPF_F_REPLACE | BPF_F_AFTER, 594 + .replace_prog_fd = fd1, 595 + ); 596 + 597 + err = bpf_prog_attach_opts(fd1, cg, atype, &opta); 598 + ASSERT_EQ(err, -EINVAL, "prog_attach"); 599 + assert_mprog_count(cg, atype, 1); 600 + cleanup: 601 + cgroup_mprog__destroy(skel); 602 + close(cg); 603 + } 604 + 605 + void test_cgroup_mprog_opts(void) 606 + { 607 + if (test__start_subtest("prog_attach_detach")) 608 + test_prog_attach_detach(BPF_CGROUP_GETSOCKOPT); 609 + if (test__start_subtest("link_attach_detach")) 610 + test_link_attach_detach(BPF_CGROUP_GETSOCKOPT); 611 + if (test__start_subtest("preorder_prog_attach_detach")) 612 + test_preorder_prog_attach_detach(BPF_CGROUP_GETSOCKOPT); 613 + if (test__start_subtest("preorder_link_attach_detach")) 614 + test_preorder_link_attach_detach(BPF_CGROUP_GETSOCKOPT); 615 + if (test__start_subtest("invalid_attach_detach")) 616 + test_invalid_attach_detach(BPF_CGROUP_GETSOCKOPT); 617 + }
+77
tools/testing/selftests/bpf/prog_tests/cgroup_mprog_ordering.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + #include <test_progs.h> 4 + #include "cgroup_helpers.h" 5 + #include "cgroup_preorder.skel.h" 6 + 7 + static int run_getsockopt_test(int cg_parent, int sock_fd, bool has_relative_fd) 8 + { 9 + LIBBPF_OPTS(bpf_prog_attach_opts, opts); 10 + enum bpf_attach_type prog_p_atype, prog_p2_atype; 11 + int prog_p_fd, prog_p2_fd; 12 + struct cgroup_preorder *skel = NULL; 13 + struct bpf_program *prog; 14 + __u8 *result, buf; 15 + socklen_t optlen; 16 + int err = 0; 17 + 18 + skel = cgroup_preorder__open_and_load(); 19 + if (!ASSERT_OK_PTR(skel, "cgroup_preorder__open_and_load")) 20 + return 0; 21 + 22 + LIBBPF_OPTS_RESET(opts); 23 + opts.flags = BPF_F_ALLOW_MULTI; 24 + prog = skel->progs.parent; 25 + prog_p_fd = bpf_program__fd(prog); 26 + prog_p_atype = bpf_program__expected_attach_type(prog); 27 + err = bpf_prog_attach_opts(prog_p_fd, cg_parent, prog_p_atype, &opts); 28 + if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent")) 29 + goto close_skel; 30 + 31 + opts.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE; 32 + if (has_relative_fd) 33 + opts.relative_fd = prog_p_fd; 34 + prog = skel->progs.parent_2; 35 + prog_p2_fd = bpf_program__fd(prog); 36 + prog_p2_atype = bpf_program__expected_attach_type(prog); 37 + err = bpf_prog_attach_opts(prog_p2_fd, cg_parent, prog_p2_atype, &opts); 38 + if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent_2")) 39 + goto detach_parent; 40 + 41 + err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 42 + if (!ASSERT_OK(err, "getsockopt")) 43 + goto detach_parent_2; 44 + 45 + result = skel->bss->result; 46 + ASSERT_TRUE(result[0] == 4 && result[1] == 3, "result values"); 47 + 48 + detach_parent_2: 49 + ASSERT_OK(bpf_prog_detach2(prog_p2_fd, cg_parent, prog_p2_atype), 50 + "bpf_prog_detach2-parent_2"); 51 + detach_parent: 52 + ASSERT_OK(bpf_prog_detach2(prog_p_fd, cg_parent, prog_p_atype), 53 + "bpf_prog_detach2-parent"); 54 + close_skel: 55 + cgroup_preorder__destroy(skel); 56 + return err; 57 + } 58 + 59 + void test_cgroup_mprog_ordering(void) 60 + { 61 + int cg_parent = -1, sock_fd = -1; 62 + 63 + cg_parent = test__join_cgroup("/parent"); 64 + if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent")) 65 + goto out; 66 + 67 + sock_fd = socket(AF_INET, SOCK_STREAM, 0); 68 + if (!ASSERT_GE(sock_fd, 0, "socket")) 69 + goto out; 70 + 71 + ASSERT_OK(run_getsockopt_test(cg_parent, sock_fd, false), "getsockopt_test_1"); 72 + ASSERT_OK(run_getsockopt_test(cg_parent, sock_fd, true), "getsockopt_test_2"); 73 + 74 + out: 75 + close(sock_fd); 76 + close(cg_parent); 77 + }
+30
tools/testing/selftests/bpf/progs/cgroup_mprog.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 + #include <vmlinux.h> 4 + #include <bpf/bpf_helpers.h> 5 + 6 + char _license[] SEC("license") = "GPL"; 7 + 8 + SEC("cgroup/getsockopt") 9 + int getsockopt_1(struct bpf_sockopt *ctx) 10 + { 11 + return 1; 12 + } 13 + 14 + SEC("cgroup/getsockopt") 15 + int getsockopt_2(struct bpf_sockopt *ctx) 16 + { 17 + return 1; 18 + } 19 + 20 + SEC("cgroup/getsockopt") 21 + int getsockopt_3(struct bpf_sockopt *ctx) 22 + { 23 + return 1; 24 + } 25 + 26 + SEC("cgroup/getsockopt") 27 + int getsockopt_4(struct bpf_sockopt *ctx) 28 + { 29 + return 1; 30 + }