1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/bpf.h> 3 #include <test_progs.h> 4 #include "cgroup_helpers.h" 5 6 static char bpf_log_buf[4096]; 7 static bool verbose; 8 9 enum sock_create_test_error { 10 OK = 0, 11 DENY_CREATE, 12 }; 13 14 static struct sock_create_test { 15 const char *descr; 16 const struct bpf_insn insns[64]; 17 enum bpf_attach_type attach_type; 18 enum bpf_attach_type expected_attach_type; 19 20 int domain; 21 int type; 22 int protocol; 23 24 int optname; 25 int optval; 26 enum sock_create_test_error error; 27 } tests[] = { 28 { 29 .descr = "AF_INET set priority", 30 .insns = { 31 /* r3 = 123 (priority) */ 32 BPF_MOV64_IMM(BPF_REG_3, 123), 33 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, 34 offsetof(struct bpf_sock, priority)), 35 36 /* return 1 */ 37 BPF_MOV64_IMM(BPF_REG_0, 1), 38 BPF_EXIT_INSN(), 39 }, 40 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 41 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 42 43 .domain = AF_INET, 44 .type = SOCK_DGRAM, 45 46 .optname = SO_PRIORITY, 47 .optval = 123, 48 }, 49 { 50 .descr = "AF_INET6 set priority", 51 .insns = { 52 /* r3 = 123 (priority) */ 53 BPF_MOV64_IMM(BPF_REG_3, 123), 54 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, 55 offsetof(struct bpf_sock, priority)), 56 57 /* return 1 */ 58 BPF_MOV64_IMM(BPF_REG_0, 1), 59 BPF_EXIT_INSN(), 60 }, 61 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 62 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 63 64 .domain = AF_INET6, 65 .type = SOCK_DGRAM, 66 67 .optname = SO_PRIORITY, 68 .optval = 123, 69 }, 70 { 71 .descr = "AF_INET set mark", 72 .insns = { 73 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 74 75 /* get uid of process */ 76 BPF_EMIT_CALL(BPF_FUNC_get_current_uid_gid), 77 BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff), 78 79 /* if uid is 0, use given mark(666), else use uid as the mark */ 80 BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), 81 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 82 BPF_MOV64_IMM(BPF_REG_3, 666), 83 84 BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), 85 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, 86 offsetof(struct bpf_sock, mark)), 87 88 /* return 1 */ 89 BPF_MOV64_IMM(BPF_REG_0, 1), 90 BPF_EXIT_INSN(), 91 }, 92 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 93 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 94 95 .domain = AF_INET, 96 .type = SOCK_DGRAM, 97 98 .optname = SO_MARK, 99 .optval = 666, 100 }, 101 { 102 .descr = "AF_INET6 set mark", 103 .insns = { 104 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 105 106 /* get uid of process */ 107 BPF_EMIT_CALL(BPF_FUNC_get_current_uid_gid), 108 BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff), 109 110 /* if uid is 0, use given mark(666), else use uid as the mark */ 111 BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), 112 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 113 BPF_MOV64_IMM(BPF_REG_3, 666), 114 115 BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), 116 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, 117 offsetof(struct bpf_sock, mark)), 118 119 /* return 1 */ 120 BPF_MOV64_IMM(BPF_REG_0, 1), 121 BPF_EXIT_INSN(), 122 }, 123 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 124 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 125 126 .domain = AF_INET6, 127 .type = SOCK_DGRAM, 128 129 .optname = SO_MARK, 130 .optval = 666, 131 }, 132 { 133 .descr = "AF_INET bound to iface", 134 .insns = { 135 /* r3 = 1 (lo interface) */ 136 BPF_MOV64_IMM(BPF_REG_3, 1), 137 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, 138 offsetof(struct bpf_sock, bound_dev_if)), 139 140 /* return 1 */ 141 BPF_MOV64_IMM(BPF_REG_0, 1), 142 BPF_EXIT_INSN(), 143 }, 144 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 145 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 146 147 .domain = AF_INET, 148 .type = SOCK_DGRAM, 149 150 .optname = SO_BINDTOIFINDEX, 151 .optval = 1, 152 }, 153 { 154 .descr = "AF_INET6 bound to iface", 155 .insns = { 156 /* r3 = 1 (lo interface) */ 157 BPF_MOV64_IMM(BPF_REG_3, 1), 158 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, 159 offsetof(struct bpf_sock, bound_dev_if)), 160 161 /* return 1 */ 162 BPF_MOV64_IMM(BPF_REG_0, 1), 163 BPF_EXIT_INSN(), 164 }, 165 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 166 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 167 168 .domain = AF_INET6, 169 .type = SOCK_DGRAM, 170 171 .optname = SO_BINDTOIFINDEX, 172 .optval = 1, 173 }, 174 { 175 .descr = "block AF_INET, SOCK_DGRAM, IPPROTO_ICMP socket", 176 .insns = { 177 BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ 178 179 /* sock->family == AF_INET */ 180 BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, 181 offsetof(struct bpf_sock, family)), 182 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, AF_INET, 5), 183 184 /* sock->type == SOCK_DGRAM */ 185 BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, 186 offsetof(struct bpf_sock, type)), 187 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, SOCK_DGRAM, 3), 188 189 /* sock->protocol == IPPROTO_ICMP */ 190 BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, 191 offsetof(struct bpf_sock, protocol)), 192 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, IPPROTO_ICMP, 1), 193 194 /* return 0 (block) */ 195 BPF_MOV64_IMM(BPF_REG_0, 0), 196 BPF_EXIT_INSN(), 197 }, 198 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 199 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 200 201 .domain = AF_INET, 202 .type = SOCK_DGRAM, 203 .protocol = IPPROTO_ICMP, 204 205 .error = DENY_CREATE, 206 }, 207 { 208 .descr = "block AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 socket", 209 .insns = { 210 BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ 211 212 /* sock->family == AF_INET6 */ 213 BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, 214 offsetof(struct bpf_sock, family)), 215 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, AF_INET6, 5), 216 217 /* sock->type == SOCK_DGRAM */ 218 BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, 219 offsetof(struct bpf_sock, type)), 220 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, SOCK_DGRAM, 3), 221 222 /* sock->protocol == IPPROTO_ICMPV6 */ 223 BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, 224 offsetof(struct bpf_sock, protocol)), 225 BPF_JMP_IMM(BPF_JNE, BPF_REG_2, IPPROTO_ICMPV6, 1), 226 227 /* return 0 (block) */ 228 BPF_MOV64_IMM(BPF_REG_0, 0), 229 BPF_EXIT_INSN(), 230 }, 231 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 232 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 233 234 .domain = AF_INET, 235 .type = SOCK_DGRAM, 236 .protocol = IPPROTO_ICMPV6, 237 238 .error = DENY_CREATE, 239 }, 240 { 241 .descr = "load w/o expected_attach_type (compat mode)", 242 .insns = { 243 /* return 1 */ 244 BPF_MOV64_IMM(BPF_REG_0, 1), 245 BPF_EXIT_INSN(), 246 }, 247 .expected_attach_type = 0, 248 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 249 250 .domain = AF_INET, 251 .type = SOCK_STREAM, 252 }, 253 }; 254 255 static int load_prog(const struct bpf_insn *insns, 256 enum bpf_attach_type expected_attach_type) 257 { 258 LIBBPF_OPTS(bpf_prog_load_opts, opts, 259 .expected_attach_type = expected_attach_type, 260 .log_level = 2, 261 .log_buf = bpf_log_buf, 262 .log_size = sizeof(bpf_log_buf), 263 ); 264 int fd, insns_cnt = 0; 265 266 for (; 267 insns[insns_cnt].code != (BPF_JMP | BPF_EXIT); 268 insns_cnt++) { 269 } 270 insns_cnt++; 271 272 fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", insns, 273 insns_cnt, &opts); 274 if (verbose && fd < 0) 275 fprintf(stderr, "%s\n", bpf_log_buf); 276 277 return fd; 278 } 279 280 static int run_test(int cgroup_fd, struct sock_create_test *test) 281 { 282 int sock_fd, err, prog_fd, optval, ret = -1; 283 socklen_t optlen = sizeof(optval); 284 285 prog_fd = load_prog(test->insns, test->expected_attach_type); 286 if (prog_fd < 0) { 287 log_err("Failed to load BPF program"); 288 return -1; 289 } 290 291 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0); 292 if (err < 0) { 293 log_err("Failed to attach BPF program"); 294 goto close_prog_fd; 295 } 296 297 sock_fd = socket(test->domain, test->type, test->protocol); 298 if (sock_fd < 0) { 299 if (test->error == DENY_CREATE) 300 ret = 0; 301 else 302 log_err("Failed to create socket"); 303 304 goto detach_prog; 305 } 306 307 if (test->optname) { 308 err = getsockopt(sock_fd, SOL_SOCKET, test->optname, &optval, &optlen); 309 if (err) { 310 log_err("Failed to call getsockopt"); 311 goto cleanup; 312 } 313 314 if (optval != test->optval) { 315 errno = 0; 316 log_err("getsockopt returned unexpected optval"); 317 goto cleanup; 318 } 319 } 320 321 ret = test->error != OK; 322 323 cleanup: 324 close(sock_fd); 325 detach_prog: 326 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type); 327 close_prog_fd: 328 close(prog_fd); 329 return ret; 330 } 331 332 void test_sock_create(void) 333 { 334 int cgroup_fd, i; 335 336 cgroup_fd = test__join_cgroup("/sock_create"); 337 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup")) 338 return; 339 340 for (i = 0; i < ARRAY_SIZE(tests); i++) { 341 if (!test__start_subtest(tests[i].descr)) 342 continue; 343 344 ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr); 345 } 346 347 close(cgroup_fd); 348 } 349