1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/bpf.h> 3 #include <test_progs.h> 4 #include "cgroup_helpers.h" 5 6 #define TEST_NS "sock_post_bind" 7 8 static char bpf_log_buf[4096]; 9 10 static struct sock_post_bind_test { 11 const char *descr; 12 /* BPF prog properties */ 13 const struct bpf_insn insns[64]; 14 enum bpf_attach_type attach_type; 15 enum bpf_attach_type expected_attach_type; 16 /* Socket properties */ 17 int domain; 18 int type; 19 /* Endpoint to bind() to */ 20 const char *ip; 21 unsigned short port; 22 unsigned short port_retry; 23 24 /* Expected test result */ 25 enum { 26 ATTACH_REJECT, 27 BIND_REJECT, 28 SUCCESS, 29 RETRY_SUCCESS, 30 RETRY_REJECT 31 } result; 32 } tests[] = { 33 { 34 .descr = "attach type mismatch bind4 vs bind6", 35 .insns = { 36 BPF_MOV64_IMM(BPF_REG_0, 1), 37 BPF_EXIT_INSN(), 38 }, 39 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 40 .attach_type = BPF_CGROUP_INET6_POST_BIND, 41 .result = ATTACH_REJECT, 42 }, 43 { 44 .descr = "attach type mismatch bind6 vs bind4", 45 .insns = { 46 BPF_MOV64_IMM(BPF_REG_0, 1), 47 BPF_EXIT_INSN(), 48 }, 49 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 50 .attach_type = BPF_CGROUP_INET4_POST_BIND, 51 .result = ATTACH_REJECT, 52 }, 53 { 54 .descr = "attach type mismatch default vs bind4", 55 .insns = { 56 BPF_MOV64_IMM(BPF_REG_0, 1), 57 BPF_EXIT_INSN(), 58 }, 59 .expected_attach_type = 0, 60 .attach_type = BPF_CGROUP_INET4_POST_BIND, 61 .result = ATTACH_REJECT, 62 }, 63 { 64 .descr = "attach type mismatch bind6 vs sock_create", 65 .insns = { 66 BPF_MOV64_IMM(BPF_REG_0, 1), 67 BPF_EXIT_INSN(), 68 }, 69 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 70 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 71 .result = ATTACH_REJECT, 72 }, 73 { 74 .descr = "bind4 reject all", 75 .insns = { 76 BPF_MOV64_IMM(BPF_REG_0, 0), 77 BPF_EXIT_INSN(), 78 }, 79 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 80 .attach_type = BPF_CGROUP_INET4_POST_BIND, 81 .domain = AF_INET, 82 .type = SOCK_STREAM, 83 .ip = "0.0.0.0", 84 .result = BIND_REJECT, 85 }, 86 { 87 .descr = "bind6 reject all", 88 .insns = { 89 BPF_MOV64_IMM(BPF_REG_0, 0), 90 BPF_EXIT_INSN(), 91 }, 92 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 93 .attach_type = BPF_CGROUP_INET6_POST_BIND, 94 .domain = AF_INET6, 95 .type = SOCK_STREAM, 96 .ip = "::", 97 .result = BIND_REJECT, 98 }, 99 { 100 .descr = "bind6 deny specific IP & port", 101 .insns = { 102 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 103 104 /* if (ip == expected && port == expected) */ 105 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 106 offsetof(struct bpf_sock, src_ip6[3])), 107 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 108 __bpf_constant_ntohl(0x00000001), 4), 109 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 110 offsetof(struct bpf_sock, src_port)), 111 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2), 112 113 /* return DENY; */ 114 BPF_MOV64_IMM(BPF_REG_0, 0), 115 BPF_JMP_A(1), 116 117 /* else return ALLOW; */ 118 BPF_MOV64_IMM(BPF_REG_0, 1), 119 BPF_EXIT_INSN(), 120 }, 121 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 122 .attach_type = BPF_CGROUP_INET6_POST_BIND, 123 .domain = AF_INET6, 124 .type = SOCK_STREAM, 125 .ip = "::1", 126 .port = 8193, 127 .result = BIND_REJECT, 128 }, 129 { 130 .descr = "bind4 allow specific IP & port", 131 .insns = { 132 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 133 134 /* if (ip == expected && port == expected) */ 135 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 136 offsetof(struct bpf_sock, src_ip4)), 137 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 138 __bpf_constant_ntohl(0x7F000001), 4), 139 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 140 offsetof(struct bpf_sock, src_port)), 141 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2), 142 143 /* return ALLOW; */ 144 BPF_MOV64_IMM(BPF_REG_0, 1), 145 BPF_JMP_A(1), 146 147 /* else return DENY; */ 148 BPF_MOV64_IMM(BPF_REG_0, 0), 149 BPF_EXIT_INSN(), 150 }, 151 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 152 .attach_type = BPF_CGROUP_INET4_POST_BIND, 153 .domain = AF_INET, 154 .type = SOCK_STREAM, 155 .ip = "127.0.0.1", 156 .port = 4098, 157 .result = SUCCESS, 158 }, 159 { 160 .descr = "bind4 deny specific IP & port of TCP, and retry", 161 .insns = { 162 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 163 164 /* if (ip == expected && port == expected) */ 165 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 166 offsetof(struct bpf_sock, src_ip4)), 167 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 168 __bpf_constant_ntohl(0x7F000001), 4), 169 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 170 offsetof(struct bpf_sock, src_port)), 171 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2), 172 173 /* return DENY; */ 174 BPF_MOV64_IMM(BPF_REG_0, 0), 175 BPF_JMP_A(1), 176 177 /* else return ALLOW; */ 178 BPF_MOV64_IMM(BPF_REG_0, 1), 179 BPF_EXIT_INSN(), 180 }, 181 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 182 .attach_type = BPF_CGROUP_INET4_POST_BIND, 183 .domain = AF_INET, 184 .type = SOCK_STREAM, 185 .ip = "127.0.0.1", 186 .port = 4098, 187 .port_retry = 5000, 188 .result = RETRY_SUCCESS, 189 }, 190 { 191 .descr = "bind4 deny specific IP & port of UDP, and retry", 192 .insns = { 193 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 194 195 /* if (ip == expected && port == expected) */ 196 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 197 offsetof(struct bpf_sock, src_ip4)), 198 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 199 __bpf_constant_ntohl(0x7F000001), 4), 200 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 201 offsetof(struct bpf_sock, src_port)), 202 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2), 203 204 /* return DENY; */ 205 BPF_MOV64_IMM(BPF_REG_0, 0), 206 BPF_JMP_A(1), 207 208 /* else return ALLOW; */ 209 BPF_MOV64_IMM(BPF_REG_0, 1), 210 BPF_EXIT_INSN(), 211 }, 212 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 213 .attach_type = BPF_CGROUP_INET4_POST_BIND, 214 .domain = AF_INET, 215 .type = SOCK_DGRAM, 216 .ip = "127.0.0.1", 217 .port = 4098, 218 .port_retry = 5000, 219 .result = RETRY_SUCCESS, 220 }, 221 { 222 .descr = "bind6 deny specific IP & port, and retry", 223 .insns = { 224 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 225 226 /* if (ip == expected && port == expected) */ 227 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 228 offsetof(struct bpf_sock, src_ip6[3])), 229 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 230 __bpf_constant_ntohl(0x00000001), 4), 231 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 232 offsetof(struct bpf_sock, src_port)), 233 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2), 234 235 /* return DENY; */ 236 BPF_MOV64_IMM(BPF_REG_0, 0), 237 BPF_JMP_A(1), 238 239 /* else return ALLOW; */ 240 BPF_MOV64_IMM(BPF_REG_0, 1), 241 BPF_EXIT_INSN(), 242 }, 243 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 244 .attach_type = BPF_CGROUP_INET6_POST_BIND, 245 .domain = AF_INET6, 246 .type = SOCK_STREAM, 247 .ip = "::1", 248 .port = 8193, 249 .port_retry = 9000, 250 .result = RETRY_SUCCESS, 251 }, 252 { 253 .descr = "bind4 allow all", 254 .insns = { 255 BPF_MOV64_IMM(BPF_REG_0, 1), 256 BPF_EXIT_INSN(), 257 }, 258 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 259 .attach_type = BPF_CGROUP_INET4_POST_BIND, 260 .domain = AF_INET, 261 .type = SOCK_STREAM, 262 .ip = "0.0.0.0", 263 .result = SUCCESS, 264 }, 265 { 266 .descr = "bind6 allow all", 267 .insns = { 268 BPF_MOV64_IMM(BPF_REG_0, 1), 269 BPF_EXIT_INSN(), 270 }, 271 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 272 .attach_type = BPF_CGROUP_INET6_POST_BIND, 273 .domain = AF_INET6, 274 .type = SOCK_STREAM, 275 .ip = "::", 276 .result = SUCCESS, 277 }, 278 }; 279 280 static int load_prog(const struct bpf_insn *insns, 281 enum bpf_attach_type expected_attach_type) 282 { 283 LIBBPF_OPTS(bpf_prog_load_opts, opts, 284 .expected_attach_type = expected_attach_type, 285 .log_level = 2, 286 .log_buf = bpf_log_buf, 287 .log_size = sizeof(bpf_log_buf), 288 ); 289 int fd, insns_cnt = 0; 290 291 for (; 292 insns[insns_cnt].code != (BPF_JMP | BPF_EXIT); 293 insns_cnt++) { 294 } 295 insns_cnt++; 296 297 fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", insns, 298 insns_cnt, &opts); 299 if (fd < 0) 300 fprintf(stderr, "%s\n", bpf_log_buf); 301 302 return fd; 303 } 304 305 static int bind_sock(int domain, int type, const char *ip, 306 unsigned short port, unsigned short port_retry) 307 { 308 struct sockaddr_storage addr; 309 struct sockaddr_in6 *addr6; 310 struct sockaddr_in *addr4; 311 int sockfd = -1; 312 socklen_t len; 313 int res = SUCCESS; 314 315 sockfd = socket(domain, type, 0); 316 if (sockfd < 0) 317 goto err; 318 319 memset(&addr, 0, sizeof(addr)); 320 321 if (domain == AF_INET) { 322 len = sizeof(struct sockaddr_in); 323 addr4 = (struct sockaddr_in *)&addr; 324 addr4->sin_family = domain; 325 addr4->sin_port = htons(port); 326 if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1) 327 goto err; 328 } else if (domain == AF_INET6) { 329 len = sizeof(struct sockaddr_in6); 330 addr6 = (struct sockaddr_in6 *)&addr; 331 addr6->sin6_family = domain; 332 addr6->sin6_port = htons(port); 333 if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1) 334 goto err; 335 } else { 336 goto err; 337 } 338 339 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) { 340 /* sys_bind() may fail for different reasons, errno has to be 341 * checked to confirm that BPF program rejected it. 342 */ 343 if (errno != EPERM) 344 goto err; 345 if (port_retry) 346 goto retry; 347 res = BIND_REJECT; 348 goto out; 349 } 350 351 goto out; 352 retry: 353 if (domain == AF_INET) 354 addr4->sin_port = htons(port_retry); 355 else 356 addr6->sin6_port = htons(port_retry); 357 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) { 358 if (errno != EPERM) 359 goto err; 360 res = RETRY_REJECT; 361 } else { 362 res = RETRY_SUCCESS; 363 } 364 goto out; 365 err: 366 res = -1; 367 out: 368 close(sockfd); 369 return res; 370 } 371 372 static int run_test(int cgroup_fd, struct sock_post_bind_test *test) 373 { 374 int err, prog_fd, res, ret = 0; 375 376 prog_fd = load_prog(test->insns, test->expected_attach_type); 377 if (prog_fd < 0) 378 goto err; 379 380 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0); 381 if (err < 0) { 382 if (test->result == ATTACH_REJECT) 383 goto out; 384 else 385 goto err; 386 } 387 388 res = bind_sock(test->domain, test->type, test->ip, test->port, 389 test->port_retry); 390 if (res > 0 && test->result == res) 391 goto out; 392 err: 393 ret = -1; 394 out: 395 /* Detaching w/o checking return code: best effort attempt. */ 396 if (prog_fd != -1) 397 bpf_prog_detach(cgroup_fd, test->attach_type); 398 close(prog_fd); 399 return ret; 400 } 401 402 void test_sock_post_bind(void) 403 { 404 struct netns_obj *ns; 405 int cgroup_fd; 406 int i; 407 408 cgroup_fd = test__join_cgroup("/post_bind"); 409 if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup")) 410 return; 411 412 ns = netns_new(TEST_NS, true); 413 if (!ASSERT_OK_PTR(ns, "netns_new")) 414 goto cleanup; 415 416 for (i = 0; i < ARRAY_SIZE(tests); i++) { 417 if (!test__start_subtest(tests[i].descr)) 418 continue; 419 420 ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr); 421 } 422 423 cleanup: 424 netns_free(ns); 425 close(cgroup_fd); 426 } 427