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