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 cg_child, int sock_fd, bool all_preorder) 8 { 9 LIBBPF_OPTS(bpf_prog_attach_opts, opts); 10 enum bpf_attach_type prog_c_atype, prog_c2_atype, prog_p_atype, prog_p2_atype; 11 int prog_c_fd, prog_c2_fd, 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 buf = 0x00; 23 err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); 24 if (!ASSERT_OK(err, "setsockopt")) 25 goto close_skel; 26 27 opts.flags = BPF_F_ALLOW_MULTI; 28 if (all_preorder) 29 opts.flags |= BPF_F_PREORDER; 30 prog = skel->progs.child; 31 prog_c_fd = bpf_program__fd(prog); 32 prog_c_atype = bpf_program__expected_attach_type(prog); 33 err = bpf_prog_attach_opts(prog_c_fd, cg_child, prog_c_atype, &opts); 34 if (!ASSERT_OK(err, "bpf_prog_attach_opts-child")) 35 goto close_skel; 36 37 opts.flags = BPF_F_ALLOW_MULTI | BPF_F_PREORDER; 38 prog = skel->progs.child_2; 39 prog_c2_fd = bpf_program__fd(prog); 40 prog_c2_atype = bpf_program__expected_attach_type(prog); 41 err = bpf_prog_attach_opts(prog_c2_fd, cg_child, prog_c2_atype, &opts); 42 if (!ASSERT_OK(err, "bpf_prog_attach_opts-child_2")) 43 goto detach_child; 44 45 optlen = 1; 46 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 47 if (!ASSERT_OK(err, "getsockopt")) 48 goto detach_child_2; 49 50 result = skel->bss->result; 51 if (all_preorder) 52 ASSERT_TRUE(result[0] == 1 && result[1] == 2, "child only"); 53 else 54 ASSERT_TRUE(result[0] == 2 && result[1] == 1, "child only"); 55 56 skel->bss->idx = 0; 57 memset(result, 0, 4); 58 59 opts.flags = BPF_F_ALLOW_MULTI; 60 if (all_preorder) 61 opts.flags |= BPF_F_PREORDER; 62 prog = skel->progs.parent; 63 prog_p_fd = bpf_program__fd(prog); 64 prog_p_atype = bpf_program__expected_attach_type(prog); 65 err = bpf_prog_attach_opts(prog_p_fd, cg_parent, prog_p_atype, &opts); 66 if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent")) 67 goto detach_child_2; 68 69 opts.flags = BPF_F_ALLOW_MULTI | BPF_F_PREORDER; 70 prog = skel->progs.parent_2; 71 prog_p2_fd = bpf_program__fd(prog); 72 prog_p2_atype = bpf_program__expected_attach_type(prog); 73 err = bpf_prog_attach_opts(prog_p2_fd, cg_parent, prog_p2_atype, &opts); 74 if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent_2")) 75 goto detach_parent; 76 77 err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); 78 if (!ASSERT_OK(err, "getsockopt")) 79 goto detach_parent_2; 80 81 if (all_preorder) 82 ASSERT_TRUE(result[0] == 3 && result[1] == 4 && result[2] == 1 && result[3] == 2, 83 "parent and child"); 84 else 85 ASSERT_TRUE(result[0] == 4 && result[1] == 2 && result[2] == 1 && result[3] == 3, 86 "parent and child"); 87 88 detach_parent_2: 89 ASSERT_OK(bpf_prog_detach2(prog_p2_fd, cg_parent, prog_p2_atype), 90 "bpf_prog_detach2-parent_2"); 91 detach_parent: 92 ASSERT_OK(bpf_prog_detach2(prog_p_fd, cg_parent, prog_p_atype), 93 "bpf_prog_detach2-parent"); 94 detach_child_2: 95 ASSERT_OK(bpf_prog_detach2(prog_c2_fd, cg_child, prog_c2_atype), 96 "bpf_prog_detach2-child_2"); 97 detach_child: 98 ASSERT_OK(bpf_prog_detach2(prog_c_fd, cg_child, prog_c_atype), 99 "bpf_prog_detach2-child"); 100 close_skel: 101 cgroup_preorder__destroy(skel); 102 return err; 103 } 104 105 void test_cgroup_preorder(void) 106 { 107 int cg_parent = -1, cg_child = -1, sock_fd = -1; 108 109 cg_parent = test__join_cgroup("/parent"); 110 if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent")) 111 goto out; 112 113 cg_child = test__join_cgroup("/parent/child"); 114 if (!ASSERT_GE(cg_child, 0, "join_cgroup /parent/child")) 115 goto out; 116 117 sock_fd = socket(AF_INET, SOCK_STREAM, 0); 118 if (!ASSERT_GE(sock_fd, 0, "socket")) 119 goto out; 120 121 ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, false), "getsockopt_test_1"); 122 ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, true), "getsockopt_test_2"); 123 124 out: 125 close(sock_fd); 126 close(cg_child); 127 close(cg_parent); 128 } 129