1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2018 Facebook 3 4 #include <stdio.h> 5 #include <unistd.h> 6 7 #include <arpa/inet.h> 8 #include <sys/types.h> 9 #include <sys/socket.h> 10 11 #include <linux/filter.h> 12 13 #include <bpf/bpf.h> 14 15 #include "cgroup_helpers.h" 16 #include <bpf/bpf_endian.h> 17 #include "bpf_rlimit.h" 18 #include "bpf_util.h" 19 20 #define CG_PATH "/foo" 21 #define MAX_INSNS 512 22 23 char bpf_log_buf[BPF_LOG_BUF_SIZE]; 24 static bool verbose = false; 25 26 struct sock_test { 27 const char *descr; 28 /* BPF prog properties */ 29 struct bpf_insn insns[MAX_INSNS]; 30 enum bpf_attach_type expected_attach_type; 31 enum bpf_attach_type attach_type; 32 /* Socket properties */ 33 int domain; 34 int type; 35 /* Endpoint to bind() to */ 36 const char *ip; 37 unsigned short port; 38 unsigned short port_retry; 39 /* Expected test result */ 40 enum { 41 LOAD_REJECT, 42 ATTACH_REJECT, 43 BIND_REJECT, 44 SUCCESS, 45 RETRY_SUCCESS, 46 RETRY_REJECT 47 } result; 48 }; 49 50 static struct sock_test tests[] = { 51 { 52 .descr = "bind4 load with invalid access: src_ip6", 53 .insns = { 54 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 55 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 56 offsetof(struct bpf_sock, src_ip6[0])), 57 BPF_MOV64_IMM(BPF_REG_0, 1), 58 BPF_EXIT_INSN(), 59 }, 60 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 61 .attach_type = BPF_CGROUP_INET4_POST_BIND, 62 .result = LOAD_REJECT, 63 }, 64 { 65 .descr = "bind4 load with invalid access: mark", 66 .insns = { 67 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 68 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 69 offsetof(struct bpf_sock, mark)), 70 BPF_MOV64_IMM(BPF_REG_0, 1), 71 BPF_EXIT_INSN(), 72 }, 73 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 74 .attach_type = BPF_CGROUP_INET4_POST_BIND, 75 .result = LOAD_REJECT, 76 }, 77 { 78 .descr = "bind6 load with invalid access: src_ip4", 79 .insns = { 80 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 81 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 82 offsetof(struct bpf_sock, src_ip4)), 83 BPF_MOV64_IMM(BPF_REG_0, 1), 84 BPF_EXIT_INSN(), 85 }, 86 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 87 .attach_type = BPF_CGROUP_INET6_POST_BIND, 88 .result = LOAD_REJECT, 89 }, 90 { 91 .descr = "sock_create load with invalid access: src_port", 92 .insns = { 93 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 94 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 95 offsetof(struct bpf_sock, src_port)), 96 BPF_MOV64_IMM(BPF_REG_0, 1), 97 BPF_EXIT_INSN(), 98 }, 99 .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, 100 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 101 .result = LOAD_REJECT, 102 }, 103 { 104 .descr = "sock_create load w/o expected_attach_type (compat mode)", 105 .insns = { 106 BPF_MOV64_IMM(BPF_REG_0, 1), 107 BPF_EXIT_INSN(), 108 }, 109 .expected_attach_type = 0, 110 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 111 .domain = AF_INET, 112 .type = SOCK_STREAM, 113 .ip = "127.0.0.1", 114 .port = 8097, 115 .result = SUCCESS, 116 }, 117 { 118 .descr = "sock_create load w/ expected_attach_type", 119 .insns = { 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 .domain = AF_INET, 126 .type = SOCK_STREAM, 127 .ip = "127.0.0.1", 128 .port = 8097, 129 .result = SUCCESS, 130 }, 131 { 132 .descr = "attach type mismatch bind4 vs bind6", 133 .insns = { 134 BPF_MOV64_IMM(BPF_REG_0, 1), 135 BPF_EXIT_INSN(), 136 }, 137 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 138 .attach_type = BPF_CGROUP_INET6_POST_BIND, 139 .result = ATTACH_REJECT, 140 }, 141 { 142 .descr = "attach type mismatch bind6 vs bind4", 143 .insns = { 144 BPF_MOV64_IMM(BPF_REG_0, 1), 145 BPF_EXIT_INSN(), 146 }, 147 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 148 .attach_type = BPF_CGROUP_INET4_POST_BIND, 149 .result = ATTACH_REJECT, 150 }, 151 { 152 .descr = "attach type mismatch default vs bind4", 153 .insns = { 154 BPF_MOV64_IMM(BPF_REG_0, 1), 155 BPF_EXIT_INSN(), 156 }, 157 .expected_attach_type = 0, 158 .attach_type = BPF_CGROUP_INET4_POST_BIND, 159 .result = ATTACH_REJECT, 160 }, 161 { 162 .descr = "attach type mismatch bind6 vs sock_create", 163 .insns = { 164 BPF_MOV64_IMM(BPF_REG_0, 1), 165 BPF_EXIT_INSN(), 166 }, 167 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 168 .attach_type = BPF_CGROUP_INET_SOCK_CREATE, 169 .result = ATTACH_REJECT, 170 }, 171 { 172 .descr = "bind4 reject all", 173 .insns = { 174 BPF_MOV64_IMM(BPF_REG_0, 0), 175 BPF_EXIT_INSN(), 176 }, 177 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 178 .attach_type = BPF_CGROUP_INET4_POST_BIND, 179 .domain = AF_INET, 180 .type = SOCK_STREAM, 181 .ip = "0.0.0.0", 182 .result = BIND_REJECT, 183 }, 184 { 185 .descr = "bind6 reject all", 186 .insns = { 187 BPF_MOV64_IMM(BPF_REG_0, 0), 188 BPF_EXIT_INSN(), 189 }, 190 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 191 .attach_type = BPF_CGROUP_INET6_POST_BIND, 192 .domain = AF_INET6, 193 .type = SOCK_STREAM, 194 .ip = "::", 195 .result = BIND_REJECT, 196 }, 197 { 198 .descr = "bind6 deny specific IP & port", 199 .insns = { 200 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 201 202 /* if (ip == expected && port == expected) */ 203 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 204 offsetof(struct bpf_sock, src_ip6[3])), 205 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 206 __bpf_constant_ntohl(0x00000001), 4), 207 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 208 offsetof(struct bpf_sock, src_port)), 209 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2), 210 211 /* return DENY; */ 212 BPF_MOV64_IMM(BPF_REG_0, 0), 213 BPF_JMP_A(1), 214 215 /* else return ALLOW; */ 216 BPF_MOV64_IMM(BPF_REG_0, 1), 217 BPF_EXIT_INSN(), 218 }, 219 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 220 .attach_type = BPF_CGROUP_INET6_POST_BIND, 221 .domain = AF_INET6, 222 .type = SOCK_STREAM, 223 .ip = "::1", 224 .port = 8193, 225 .result = BIND_REJECT, 226 }, 227 { 228 .descr = "bind4 allow specific IP & port", 229 .insns = { 230 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 231 232 /* if (ip == expected && port == expected) */ 233 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 234 offsetof(struct bpf_sock, src_ip4)), 235 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 236 __bpf_constant_ntohl(0x7F000001), 4), 237 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 238 offsetof(struct bpf_sock, src_port)), 239 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2), 240 241 /* return ALLOW; */ 242 BPF_MOV64_IMM(BPF_REG_0, 1), 243 BPF_JMP_A(1), 244 245 /* else return DENY; */ 246 BPF_MOV64_IMM(BPF_REG_0, 0), 247 BPF_EXIT_INSN(), 248 }, 249 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 250 .attach_type = BPF_CGROUP_INET4_POST_BIND, 251 .domain = AF_INET, 252 .type = SOCK_STREAM, 253 .ip = "127.0.0.1", 254 .port = 4098, 255 .result = SUCCESS, 256 }, 257 { 258 .descr = "bind4 deny specific IP & port of TCP, and retry", 259 .insns = { 260 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 261 262 /* if (ip == expected && port == expected) */ 263 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 264 offsetof(struct bpf_sock, src_ip4)), 265 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 266 __bpf_constant_ntohl(0x7F000001), 4), 267 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 268 offsetof(struct bpf_sock, src_port)), 269 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2), 270 271 /* return DENY; */ 272 BPF_MOV64_IMM(BPF_REG_0, 0), 273 BPF_JMP_A(1), 274 275 /* else return ALLOW; */ 276 BPF_MOV64_IMM(BPF_REG_0, 1), 277 BPF_EXIT_INSN(), 278 }, 279 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 280 .attach_type = BPF_CGROUP_INET4_POST_BIND, 281 .domain = AF_INET, 282 .type = SOCK_STREAM, 283 .ip = "127.0.0.1", 284 .port = 4098, 285 .port_retry = 5000, 286 .result = RETRY_SUCCESS, 287 }, 288 { 289 .descr = "bind4 deny specific IP & port of UDP, and retry", 290 .insns = { 291 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 292 293 /* if (ip == expected && port == expected) */ 294 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 295 offsetof(struct bpf_sock, src_ip4)), 296 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 297 __bpf_constant_ntohl(0x7F000001), 4), 298 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 299 offsetof(struct bpf_sock, src_port)), 300 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2), 301 302 /* return DENY; */ 303 BPF_MOV64_IMM(BPF_REG_0, 0), 304 BPF_JMP_A(1), 305 306 /* else return ALLOW; */ 307 BPF_MOV64_IMM(BPF_REG_0, 1), 308 BPF_EXIT_INSN(), 309 }, 310 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 311 .attach_type = BPF_CGROUP_INET4_POST_BIND, 312 .domain = AF_INET, 313 .type = SOCK_DGRAM, 314 .ip = "127.0.0.1", 315 .port = 4098, 316 .port_retry = 5000, 317 .result = RETRY_SUCCESS, 318 }, 319 { 320 .descr = "bind6 deny specific IP & port, and retry", 321 .insns = { 322 BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 323 324 /* if (ip == expected && port == expected) */ 325 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 326 offsetof(struct bpf_sock, src_ip6[3])), 327 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 328 __bpf_constant_ntohl(0x00000001), 4), 329 BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 330 offsetof(struct bpf_sock, src_port)), 331 BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2), 332 333 /* return DENY; */ 334 BPF_MOV64_IMM(BPF_REG_0, 0), 335 BPF_JMP_A(1), 336 337 /* else return ALLOW; */ 338 BPF_MOV64_IMM(BPF_REG_0, 1), 339 BPF_EXIT_INSN(), 340 }, 341 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 342 .attach_type = BPF_CGROUP_INET6_POST_BIND, 343 .domain = AF_INET6, 344 .type = SOCK_STREAM, 345 .ip = "::1", 346 .port = 8193, 347 .port_retry = 9000, 348 .result = RETRY_SUCCESS, 349 }, 350 { 351 .descr = "bind4 allow all", 352 .insns = { 353 BPF_MOV64_IMM(BPF_REG_0, 1), 354 BPF_EXIT_INSN(), 355 }, 356 .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, 357 .attach_type = BPF_CGROUP_INET4_POST_BIND, 358 .domain = AF_INET, 359 .type = SOCK_STREAM, 360 .ip = "0.0.0.0", 361 .result = SUCCESS, 362 }, 363 { 364 .descr = "bind6 allow all", 365 .insns = { 366 BPF_MOV64_IMM(BPF_REG_0, 1), 367 BPF_EXIT_INSN(), 368 }, 369 .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, 370 .attach_type = BPF_CGROUP_INET6_POST_BIND, 371 .domain = AF_INET6, 372 .type = SOCK_STREAM, 373 .ip = "::", 374 .result = SUCCESS, 375 }, 376 }; 377 378 static size_t probe_prog_length(const struct bpf_insn *fp) 379 { 380 size_t len; 381 382 for (len = MAX_INSNS - 1; len > 0; --len) 383 if (fp[len].code != 0 || fp[len].imm != 0) 384 break; 385 return len + 1; 386 } 387 388 static int load_sock_prog(const struct bpf_insn *prog, 389 enum bpf_attach_type attach_type) 390 { 391 LIBBPF_OPTS(bpf_prog_load_opts, opts); 392 int ret, insn_cnt; 393 394 insn_cnt = probe_prog_length(prog); 395 396 opts.expected_attach_type = attach_type; 397 opts.log_buf = bpf_log_buf; 398 opts.log_size = BPF_LOG_BUF_SIZE; 399 opts.log_level = 2; 400 401 ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", prog, insn_cnt, &opts); 402 if (verbose && ret < 0) 403 fprintf(stderr, "%s\n", bpf_log_buf); 404 405 return ret; 406 } 407 408 static int attach_sock_prog(int cgfd, int progfd, 409 enum bpf_attach_type attach_type) 410 { 411 return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE); 412 } 413 414 static int bind_sock(int domain, int type, const char *ip, 415 unsigned short port, unsigned short port_retry) 416 { 417 struct sockaddr_storage addr; 418 struct sockaddr_in6 *addr6; 419 struct sockaddr_in *addr4; 420 int sockfd = -1; 421 socklen_t len; 422 int res = SUCCESS; 423 424 sockfd = socket(domain, type, 0); 425 if (sockfd < 0) 426 goto err; 427 428 memset(&addr, 0, sizeof(addr)); 429 430 if (domain == AF_INET) { 431 len = sizeof(struct sockaddr_in); 432 addr4 = (struct sockaddr_in *)&addr; 433 addr4->sin_family = domain; 434 addr4->sin_port = htons(port); 435 if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1) 436 goto err; 437 } else if (domain == AF_INET6) { 438 len = sizeof(struct sockaddr_in6); 439 addr6 = (struct sockaddr_in6 *)&addr; 440 addr6->sin6_family = domain; 441 addr6->sin6_port = htons(port); 442 if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1) 443 goto err; 444 } else { 445 goto err; 446 } 447 448 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) { 449 /* sys_bind() may fail for different reasons, errno has to be 450 * checked to confirm that BPF program rejected it. 451 */ 452 if (errno != EPERM) 453 goto err; 454 if (port_retry) 455 goto retry; 456 res = BIND_REJECT; 457 goto out; 458 } 459 460 goto out; 461 retry: 462 if (domain == AF_INET) 463 addr4->sin_port = htons(port_retry); 464 else 465 addr6->sin6_port = htons(port_retry); 466 if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) { 467 if (errno != EPERM) 468 goto err; 469 res = RETRY_REJECT; 470 } else { 471 res = RETRY_SUCCESS; 472 } 473 goto out; 474 err: 475 res = -1; 476 out: 477 close(sockfd); 478 return res; 479 } 480 481 static int run_test_case(int cgfd, const struct sock_test *test) 482 { 483 int progfd = -1; 484 int err = 0; 485 int res; 486 487 printf("Test case: %s .. ", test->descr); 488 progfd = load_sock_prog(test->insns, test->expected_attach_type); 489 if (progfd < 0) { 490 if (test->result == LOAD_REJECT) 491 goto out; 492 else 493 goto err; 494 } 495 496 if (attach_sock_prog(cgfd, progfd, test->attach_type) == -1) { 497 if (test->result == ATTACH_REJECT) 498 goto out; 499 else 500 goto err; 501 } 502 503 res = bind_sock(test->domain, test->type, test->ip, test->port, 504 test->port_retry); 505 if (res > 0 && test->result == res) 506 goto out; 507 508 err: 509 err = -1; 510 out: 511 /* Detaching w/o checking return code: best effort attempt. */ 512 if (progfd != -1) 513 bpf_prog_detach(cgfd, test->attach_type); 514 close(progfd); 515 printf("[%s]\n", err ? "FAIL" : "PASS"); 516 return err; 517 } 518 519 static int run_tests(int cgfd) 520 { 521 int passes = 0; 522 int fails = 0; 523 int i; 524 525 for (i = 0; i < ARRAY_SIZE(tests); ++i) { 526 if (run_test_case(cgfd, &tests[i])) 527 ++fails; 528 else 529 ++passes; 530 } 531 printf("Summary: %d PASSED, %d FAILED\n", passes, fails); 532 return fails ? -1 : 0; 533 } 534 535 int main(int argc, char **argv) 536 { 537 int cgfd = -1; 538 int err = 0; 539 540 cgfd = cgroup_setup_and_join(CG_PATH); 541 if (cgfd < 0) 542 goto err; 543 544 if (run_tests(cgfd)) 545 goto err; 546 547 goto out; 548 err: 549 err = -1; 550 out: 551 close(cgfd); 552 cleanup_cgroup_environment(); 553 return err; 554 } 555