1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <test_progs.h> 6 #include <bpf/btf.h> 7 8 #include "lsm_cgroup.skel.h" 9 #include "cgroup_helpers.h" 10 #include "network_helpers.h" 11 12 static struct btf *btf; 13 14 static __u32 query_prog_cnt(int cgroup_fd, const char *attach_func) 15 { 16 LIBBPF_OPTS(bpf_prog_query_opts, p); 17 int cnt = 0; 18 int i; 19 20 ASSERT_OK(bpf_prog_query_opts(cgroup_fd, BPF_LSM_CGROUP, &p), "prog_query"); 21 22 if (!attach_func) 23 return p.prog_cnt; 24 25 /* When attach_func is provided, count the number of progs that 26 * attach to the given symbol. 27 */ 28 29 if (!btf) 30 btf = btf__load_vmlinux_btf(); 31 if (!ASSERT_OK(libbpf_get_error(btf), "btf_vmlinux")) 32 return -1; 33 34 p.prog_ids = malloc(sizeof(u32) * p.prog_cnt); 35 p.prog_attach_flags = malloc(sizeof(u32) * p.prog_cnt); 36 ASSERT_OK(bpf_prog_query_opts(cgroup_fd, BPF_LSM_CGROUP, &p), "prog_query"); 37 38 for (i = 0; i < p.prog_cnt; i++) { 39 struct bpf_prog_info info = {}; 40 __u32 info_len = sizeof(info); 41 int fd; 42 43 fd = bpf_prog_get_fd_by_id(p.prog_ids[i]); 44 ASSERT_GE(fd, 0, "prog_get_fd_by_id"); 45 ASSERT_OK(bpf_obj_get_info_by_fd(fd, &info, &info_len), "prog_info_by_fd"); 46 close(fd); 47 48 if (info.attach_btf_id == 49 btf__find_by_name_kind(btf, attach_func, BTF_KIND_FUNC)) 50 cnt++; 51 } 52 53 free(p.prog_ids); 54 free(p.prog_attach_flags); 55 56 return cnt; 57 } 58 59 static void test_lsm_cgroup_functional(void) 60 { 61 DECLARE_LIBBPF_OPTS(bpf_prog_attach_opts, attach_opts); 62 DECLARE_LIBBPF_OPTS(bpf_link_update_opts, update_opts); 63 int cgroup_fd = -1, cgroup_fd2 = -1, cgroup_fd3 = -1; 64 int listen_fd, client_fd, accepted_fd; 65 struct lsm_cgroup *skel = NULL; 66 int post_create_prog_fd2 = -1; 67 int post_create_prog_fd = -1; 68 int bind_link_fd2 = -1; 69 int bind_prog_fd2 = -1; 70 int alloc_prog_fd = -1; 71 int bind_prog_fd = -1; 72 int bind_link_fd = -1; 73 int clone_prog_fd = -1; 74 int err, fd, prio; 75 socklen_t socklen; 76 77 cgroup_fd3 = test__join_cgroup("/sock_policy_empty"); 78 if (!ASSERT_GE(cgroup_fd3, 0, "create empty cgroup")) 79 goto close_cgroup; 80 81 cgroup_fd2 = test__join_cgroup("/sock_policy_reuse"); 82 if (!ASSERT_GE(cgroup_fd2, 0, "create cgroup for reuse")) 83 goto close_cgroup; 84 85 cgroup_fd = test__join_cgroup("/sock_policy"); 86 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup")) 87 goto close_cgroup; 88 89 skel = lsm_cgroup__open_and_load(); 90 if (!ASSERT_OK_PTR(skel, "open_and_load")) 91 goto close_cgroup; 92 93 post_create_prog_fd = bpf_program__fd(skel->progs.socket_post_create); 94 post_create_prog_fd2 = bpf_program__fd(skel->progs.socket_post_create2); 95 bind_prog_fd = bpf_program__fd(skel->progs.socket_bind); 96 bind_prog_fd2 = bpf_program__fd(skel->progs.socket_bind2); 97 alloc_prog_fd = bpf_program__fd(skel->progs.socket_alloc); 98 clone_prog_fd = bpf_program__fd(skel->progs.socket_clone); 99 100 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_sk_alloc_security"), 0, "prog count"); 101 ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 0, "total prog count"); 102 err = bpf_prog_attach(alloc_prog_fd, cgroup_fd, BPF_LSM_CGROUP, 0); 103 if (!ASSERT_OK(err, "attach alloc_prog_fd")) 104 goto detach_cgroup; 105 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_sk_alloc_security"), 1, "prog count"); 106 ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 1, "total prog count"); 107 108 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_inet_csk_clone"), 0, "prog count"); 109 err = bpf_prog_attach(clone_prog_fd, cgroup_fd, BPF_LSM_CGROUP, 0); 110 if (!ASSERT_OK(err, "attach clone_prog_fd")) 111 goto detach_cgroup; 112 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_inet_csk_clone"), 1, "prog count"); 113 ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 2, "total prog count"); 114 115 /* Make sure replacing works. */ 116 117 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_socket_post_create"), 0, "prog count"); 118 err = bpf_prog_attach(post_create_prog_fd, cgroup_fd, 119 BPF_LSM_CGROUP, 0); 120 if (!ASSERT_OK(err, "attach post_create_prog_fd")) 121 goto detach_cgroup; 122 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_socket_post_create"), 1, "prog count"); 123 ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 3, "total prog count"); 124 125 attach_opts.replace_prog_fd = post_create_prog_fd; 126 err = bpf_prog_attach_opts(post_create_prog_fd2, cgroup_fd, 127 BPF_LSM_CGROUP, &attach_opts); 128 if (!ASSERT_OK(err, "prog replace post_create_prog_fd")) 129 goto detach_cgroup; 130 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_socket_post_create"), 1, "prog count"); 131 ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 3, "total prog count"); 132 133 /* Try the same attach/replace via link API. */ 134 135 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_socket_bind"), 0, "prog count"); 136 bind_link_fd = bpf_link_create(bind_prog_fd, cgroup_fd, 137 BPF_LSM_CGROUP, NULL); 138 if (!ASSERT_GE(bind_link_fd, 0, "link create bind_prog_fd")) 139 goto detach_cgroup; 140 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_socket_bind"), 1, "prog count"); 141 ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 4, "total prog count"); 142 143 update_opts.old_prog_fd = bind_prog_fd; 144 update_opts.flags = BPF_F_REPLACE; 145 146 err = bpf_link_update(bind_link_fd, bind_prog_fd2, &update_opts); 147 if (!ASSERT_OK(err, "link update bind_prog_fd")) 148 goto detach_cgroup; 149 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_socket_bind"), 1, "prog count"); 150 ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 4, "total prog count"); 151 152 /* Attach another instance of bind program to another cgroup. 153 * This should trigger the reuse of the trampoline shim (two 154 * programs attaching to the same btf_id). 155 */ 156 157 ASSERT_EQ(query_prog_cnt(cgroup_fd, "bpf_lsm_socket_bind"), 1, "prog count"); 158 ASSERT_EQ(query_prog_cnt(cgroup_fd2, "bpf_lsm_socket_bind"), 0, "prog count"); 159 bind_link_fd2 = bpf_link_create(bind_prog_fd2, cgroup_fd2, 160 BPF_LSM_CGROUP, NULL); 161 if (!ASSERT_GE(bind_link_fd2, 0, "link create bind_prog_fd2")) 162 goto detach_cgroup; 163 ASSERT_EQ(query_prog_cnt(cgroup_fd2, "bpf_lsm_socket_bind"), 1, "prog count"); 164 ASSERT_EQ(query_prog_cnt(cgroup_fd, NULL), 4, "total prog count"); 165 ASSERT_EQ(query_prog_cnt(cgroup_fd2, NULL), 1, "total prog count"); 166 167 /* AF_UNIX is prohibited. */ 168 169 fd = socket(AF_UNIX, SOCK_STREAM, 0); 170 ASSERT_LT(fd, 0, "socket(AF_UNIX)"); 171 close(fd); 172 173 /* AF_INET6 gets default policy (sk_priority). */ 174 175 fd = socket(AF_INET6, SOCK_STREAM, 0); 176 if (!ASSERT_GE(fd, 0, "socket(SOCK_STREAM)")) 177 goto detach_cgroup; 178 179 prio = 0; 180 socklen = sizeof(prio); 181 ASSERT_GE(getsockopt(fd, SOL_SOCKET, SO_PRIORITY, &prio, &socklen), 0, 182 "getsockopt"); 183 ASSERT_EQ(prio, 123, "sk_priority"); 184 185 close(fd); 186 187 /* TX-only AF_PACKET is allowed. */ 188 189 ASSERT_LT(socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)), 0, 190 "socket(AF_PACKET, ..., ETH_P_ALL)"); 191 192 fd = socket(AF_PACKET, SOCK_RAW, 0); 193 ASSERT_GE(fd, 0, "socket(AF_PACKET, ..., 0)"); 194 195 /* TX-only AF_PACKET can not be rebound. */ 196 197 struct sockaddr_ll sa = { 198 .sll_family = AF_PACKET, 199 .sll_protocol = htons(ETH_P_ALL), 200 }; 201 ASSERT_LT(bind(fd, (struct sockaddr *)&sa, sizeof(sa)), 0, 202 "bind(ETH_P_ALL)"); 203 204 close(fd); 205 206 /* Trigger passive open. */ 207 208 listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0); 209 ASSERT_GE(listen_fd, 0, "start_server"); 210 client_fd = connect_to_fd(listen_fd, 0); 211 ASSERT_GE(client_fd, 0, "connect_to_fd"); 212 accepted_fd = accept(listen_fd, NULL, NULL); 213 ASSERT_GE(accepted_fd, 0, "accept"); 214 215 prio = 0; 216 socklen = sizeof(prio); 217 ASSERT_GE(getsockopt(accepted_fd, SOL_SOCKET, SO_PRIORITY, &prio, &socklen), 0, 218 "getsockopt"); 219 ASSERT_EQ(prio, 234, "sk_priority"); 220 221 /* These are replaced and never called. */ 222 ASSERT_EQ(skel->bss->called_socket_post_create, 0, "called_create"); 223 ASSERT_EQ(skel->bss->called_socket_bind, 0, "called_bind"); 224 225 /* AF_INET6+SOCK_STREAM 226 * AF_PACKET+SOCK_RAW 227 * listen_fd 228 * client_fd 229 * accepted_fd 230 */ 231 ASSERT_EQ(skel->bss->called_socket_post_create2, 5, "called_create2"); 232 233 /* start_server 234 * bind(ETH_P_ALL) 235 */ 236 ASSERT_EQ(skel->bss->called_socket_bind2, 2, "called_bind2"); 237 /* Single accept(). */ 238 ASSERT_EQ(skel->bss->called_socket_clone, 1, "called_clone"); 239 240 /* AF_UNIX+SOCK_STREAM (failed) 241 * AF_INET6+SOCK_STREAM 242 * AF_PACKET+SOCK_RAW (failed) 243 * AF_PACKET+SOCK_RAW 244 * listen_fd 245 * client_fd 246 * accepted_fd 247 */ 248 ASSERT_EQ(skel->bss->called_socket_alloc, 7, "called_alloc"); 249 250 close(listen_fd); 251 close(client_fd); 252 close(accepted_fd); 253 254 /* Make sure other cgroup doesn't trigger the programs. */ 255 256 if (!ASSERT_OK(join_cgroup("/sock_policy_empty"), "join root cgroup")) 257 goto detach_cgroup; 258 259 fd = socket(AF_INET6, SOCK_STREAM, 0); 260 if (!ASSERT_GE(fd, 0, "socket(SOCK_STREAM)")) 261 goto detach_cgroup; 262 263 prio = 0; 264 socklen = sizeof(prio); 265 ASSERT_GE(getsockopt(fd, SOL_SOCKET, SO_PRIORITY, &prio, &socklen), 0, 266 "getsockopt"); 267 ASSERT_EQ(prio, 0, "sk_priority"); 268 269 close(fd); 270 271 detach_cgroup: 272 ASSERT_GE(bpf_prog_detach2(post_create_prog_fd2, cgroup_fd, 273 BPF_LSM_CGROUP), 0, "detach_create"); 274 close(bind_link_fd); 275 /* Don't close bind_link_fd2, exercise cgroup release cleanup. */ 276 ASSERT_GE(bpf_prog_detach2(alloc_prog_fd, cgroup_fd, 277 BPF_LSM_CGROUP), 0, "detach_alloc"); 278 ASSERT_GE(bpf_prog_detach2(clone_prog_fd, cgroup_fd, 279 BPF_LSM_CGROUP), 0, "detach_clone"); 280 281 close_cgroup: 282 close(cgroup_fd); 283 close(cgroup_fd2); 284 close(cgroup_fd3); 285 lsm_cgroup__destroy(skel); 286 } 287 288 void test_lsm_cgroup(void) 289 { 290 if (test__start_subtest("functional")) 291 test_lsm_cgroup_functional(); 292 btf__free(btf); 293 } 294