Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (c) 2018 Facebook
0003 
0004 #include <stdio.h>
0005 #include <unistd.h>
0006 
0007 #include <arpa/inet.h>
0008 #include <sys/types.h>
0009 #include <sys/socket.h>
0010 
0011 #include <linux/filter.h>
0012 
0013 #include <bpf/bpf.h>
0014 
0015 #include "cgroup_helpers.h"
0016 #include <bpf/bpf_endian.h>
0017 #include "bpf_util.h"
0018 
0019 #define CG_PATH     "/foo"
0020 #define MAX_INSNS   512
0021 
0022 char bpf_log_buf[BPF_LOG_BUF_SIZE];
0023 static bool verbose = false;
0024 
0025 struct sock_test {
0026     const char *descr;
0027     /* BPF prog properties */
0028     struct bpf_insn insns[MAX_INSNS];
0029     enum bpf_attach_type expected_attach_type;
0030     enum bpf_attach_type attach_type;
0031     /* Socket properties */
0032     int domain;
0033     int type;
0034     /* Endpoint to bind() to */
0035     const char *ip;
0036     unsigned short port;
0037     unsigned short port_retry;
0038     /* Expected test result */
0039     enum {
0040         LOAD_REJECT,
0041         ATTACH_REJECT,
0042         BIND_REJECT,
0043         SUCCESS,
0044         RETRY_SUCCESS,
0045         RETRY_REJECT
0046     } result;
0047 };
0048 
0049 static struct sock_test tests[] = {
0050     {
0051         .descr = "bind4 load with invalid access: src_ip6",
0052         .insns = {
0053             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0054             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0055                     offsetof(struct bpf_sock, src_ip6[0])),
0056             BPF_MOV64_IMM(BPF_REG_0, 1),
0057             BPF_EXIT_INSN(),
0058         },
0059         .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
0060         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0061         .result = LOAD_REJECT,
0062     },
0063     {
0064         .descr = "bind4 load with invalid access: mark",
0065         .insns = {
0066             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0067             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0068                     offsetof(struct bpf_sock, mark)),
0069             BPF_MOV64_IMM(BPF_REG_0, 1),
0070             BPF_EXIT_INSN(),
0071         },
0072         .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
0073         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0074         .result = LOAD_REJECT,
0075     },
0076     {
0077         .descr = "bind6 load with invalid access: src_ip4",
0078         .insns = {
0079             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0080             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0081                     offsetof(struct bpf_sock, src_ip4)),
0082             BPF_MOV64_IMM(BPF_REG_0, 1),
0083             BPF_EXIT_INSN(),
0084         },
0085         .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
0086         .attach_type = BPF_CGROUP_INET6_POST_BIND,
0087         .result = LOAD_REJECT,
0088     },
0089     {
0090         .descr = "sock_create load with invalid access: src_port",
0091         .insns = {
0092             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0093             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0094                     offsetof(struct bpf_sock, src_port)),
0095             BPF_MOV64_IMM(BPF_REG_0, 1),
0096             BPF_EXIT_INSN(),
0097         },
0098         .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
0099         .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
0100         .result = LOAD_REJECT,
0101     },
0102     {
0103         .descr = "sock_create load w/o expected_attach_type (compat mode)",
0104         .insns = {
0105             BPF_MOV64_IMM(BPF_REG_0, 1),
0106             BPF_EXIT_INSN(),
0107         },
0108         .expected_attach_type = 0,
0109         .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
0110         .domain = AF_INET,
0111         .type = SOCK_STREAM,
0112         .ip = "127.0.0.1",
0113         .port = 8097,
0114         .result = SUCCESS,
0115     },
0116     {
0117         .descr = "sock_create load w/ expected_attach_type",
0118         .insns = {
0119             BPF_MOV64_IMM(BPF_REG_0, 1),
0120             BPF_EXIT_INSN(),
0121         },
0122         .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,
0123         .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
0124         .domain = AF_INET,
0125         .type = SOCK_STREAM,
0126         .ip = "127.0.0.1",
0127         .port = 8097,
0128         .result = SUCCESS,
0129     },
0130     {
0131         .descr = "attach type mismatch bind4 vs bind6",
0132         .insns = {
0133             BPF_MOV64_IMM(BPF_REG_0, 1),
0134             BPF_EXIT_INSN(),
0135         },
0136         .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
0137         .attach_type = BPF_CGROUP_INET6_POST_BIND,
0138         .result = ATTACH_REJECT,
0139     },
0140     {
0141         .descr = "attach type mismatch bind6 vs bind4",
0142         .insns = {
0143             BPF_MOV64_IMM(BPF_REG_0, 1),
0144             BPF_EXIT_INSN(),
0145         },
0146         .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
0147         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0148         .result = ATTACH_REJECT,
0149     },
0150     {
0151         .descr = "attach type mismatch default vs bind4",
0152         .insns = {
0153             BPF_MOV64_IMM(BPF_REG_0, 1),
0154             BPF_EXIT_INSN(),
0155         },
0156         .expected_attach_type = 0,
0157         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0158         .result = ATTACH_REJECT,
0159     },
0160     {
0161         .descr = "attach type mismatch bind6 vs sock_create",
0162         .insns = {
0163             BPF_MOV64_IMM(BPF_REG_0, 1),
0164             BPF_EXIT_INSN(),
0165         },
0166         .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
0167         .attach_type = BPF_CGROUP_INET_SOCK_CREATE,
0168         .result = ATTACH_REJECT,
0169     },
0170     {
0171         .descr = "bind4 reject all",
0172         .insns = {
0173             BPF_MOV64_IMM(BPF_REG_0, 0),
0174             BPF_EXIT_INSN(),
0175         },
0176         .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
0177         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0178         .domain = AF_INET,
0179         .type = SOCK_STREAM,
0180         .ip = "0.0.0.0",
0181         .result = BIND_REJECT,
0182     },
0183     {
0184         .descr = "bind6 reject all",
0185         .insns = {
0186             BPF_MOV64_IMM(BPF_REG_0, 0),
0187             BPF_EXIT_INSN(),
0188         },
0189         .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
0190         .attach_type = BPF_CGROUP_INET6_POST_BIND,
0191         .domain = AF_INET6,
0192         .type = SOCK_STREAM,
0193         .ip = "::",
0194         .result = BIND_REJECT,
0195     },
0196     {
0197         .descr = "bind6 deny specific IP & port",
0198         .insns = {
0199             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0200 
0201             /* if (ip == expected && port == expected) */
0202             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0203                     offsetof(struct bpf_sock, src_ip6[3])),
0204             BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
0205                     __bpf_constant_ntohl(0x00000001), 4),
0206             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0207                     offsetof(struct bpf_sock, src_port)),
0208             BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
0209 
0210             /* return DENY; */
0211             BPF_MOV64_IMM(BPF_REG_0, 0),
0212             BPF_JMP_A(1),
0213 
0214             /* else return ALLOW; */
0215             BPF_MOV64_IMM(BPF_REG_0, 1),
0216             BPF_EXIT_INSN(),
0217         },
0218         .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
0219         .attach_type = BPF_CGROUP_INET6_POST_BIND,
0220         .domain = AF_INET6,
0221         .type = SOCK_STREAM,
0222         .ip = "::1",
0223         .port = 8193,
0224         .result = BIND_REJECT,
0225     },
0226     {
0227         .descr = "bind4 allow specific IP & port",
0228         .insns = {
0229             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0230 
0231             /* if (ip == expected && port == expected) */
0232             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0233                     offsetof(struct bpf_sock, src_ip4)),
0234             BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
0235                     __bpf_constant_ntohl(0x7F000001), 4),
0236             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0237                     offsetof(struct bpf_sock, src_port)),
0238             BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
0239 
0240             /* return ALLOW; */
0241             BPF_MOV64_IMM(BPF_REG_0, 1),
0242             BPF_JMP_A(1),
0243 
0244             /* else return DENY; */
0245             BPF_MOV64_IMM(BPF_REG_0, 0),
0246             BPF_EXIT_INSN(),
0247         },
0248         .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
0249         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0250         .domain = AF_INET,
0251         .type = SOCK_STREAM,
0252         .ip = "127.0.0.1",
0253         .port = 4098,
0254         .result = SUCCESS,
0255     },
0256     {
0257         .descr = "bind4 deny specific IP & port of TCP, and retry",
0258         .insns = {
0259             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0260 
0261             /* if (ip == expected && port == expected) */
0262             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0263                     offsetof(struct bpf_sock, src_ip4)),
0264             BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
0265                     __bpf_constant_ntohl(0x7F000001), 4),
0266             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0267                     offsetof(struct bpf_sock, src_port)),
0268             BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
0269 
0270             /* return DENY; */
0271             BPF_MOV64_IMM(BPF_REG_0, 0),
0272             BPF_JMP_A(1),
0273 
0274             /* else return ALLOW; */
0275             BPF_MOV64_IMM(BPF_REG_0, 1),
0276             BPF_EXIT_INSN(),
0277         },
0278         .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
0279         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0280         .domain = AF_INET,
0281         .type = SOCK_STREAM,
0282         .ip = "127.0.0.1",
0283         .port = 4098,
0284         .port_retry = 5000,
0285         .result = RETRY_SUCCESS,
0286     },
0287     {
0288         .descr = "bind4 deny specific IP & port of UDP, and retry",
0289         .insns = {
0290             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0291 
0292             /* if (ip == expected && port == expected) */
0293             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0294                     offsetof(struct bpf_sock, src_ip4)),
0295             BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
0296                     __bpf_constant_ntohl(0x7F000001), 4),
0297             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0298                     offsetof(struct bpf_sock, src_port)),
0299             BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
0300 
0301             /* return DENY; */
0302             BPF_MOV64_IMM(BPF_REG_0, 0),
0303             BPF_JMP_A(1),
0304 
0305             /* else return ALLOW; */
0306             BPF_MOV64_IMM(BPF_REG_0, 1),
0307             BPF_EXIT_INSN(),
0308         },
0309         .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
0310         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0311         .domain = AF_INET,
0312         .type = SOCK_DGRAM,
0313         .ip = "127.0.0.1",
0314         .port = 4098,
0315         .port_retry = 5000,
0316         .result = RETRY_SUCCESS,
0317     },
0318     {
0319         .descr = "bind6 deny specific IP & port, and retry",
0320         .insns = {
0321             BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
0322 
0323             /* if (ip == expected && port == expected) */
0324             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0325                     offsetof(struct bpf_sock, src_ip6[3])),
0326             BPF_JMP_IMM(BPF_JNE, BPF_REG_7,
0327                     __bpf_constant_ntohl(0x00000001), 4),
0328             BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
0329                     offsetof(struct bpf_sock, src_port)),
0330             BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
0331 
0332             /* return DENY; */
0333             BPF_MOV64_IMM(BPF_REG_0, 0),
0334             BPF_JMP_A(1),
0335 
0336             /* else return ALLOW; */
0337             BPF_MOV64_IMM(BPF_REG_0, 1),
0338             BPF_EXIT_INSN(),
0339         },
0340         .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
0341         .attach_type = BPF_CGROUP_INET6_POST_BIND,
0342         .domain = AF_INET6,
0343         .type = SOCK_STREAM,
0344         .ip = "::1",
0345         .port = 8193,
0346         .port_retry = 9000,
0347         .result = RETRY_SUCCESS,
0348     },
0349     {
0350         .descr = "bind4 allow all",
0351         .insns = {
0352             BPF_MOV64_IMM(BPF_REG_0, 1),
0353             BPF_EXIT_INSN(),
0354         },
0355         .expected_attach_type = BPF_CGROUP_INET4_POST_BIND,
0356         .attach_type = BPF_CGROUP_INET4_POST_BIND,
0357         .domain = AF_INET,
0358         .type = SOCK_STREAM,
0359         .ip = "0.0.0.0",
0360         .result = SUCCESS,
0361     },
0362     {
0363         .descr = "bind6 allow all",
0364         .insns = {
0365             BPF_MOV64_IMM(BPF_REG_0, 1),
0366             BPF_EXIT_INSN(),
0367         },
0368         .expected_attach_type = BPF_CGROUP_INET6_POST_BIND,
0369         .attach_type = BPF_CGROUP_INET6_POST_BIND,
0370         .domain = AF_INET6,
0371         .type = SOCK_STREAM,
0372         .ip = "::",
0373         .result = SUCCESS,
0374     },
0375 };
0376 
0377 static size_t probe_prog_length(const struct bpf_insn *fp)
0378 {
0379     size_t len;
0380 
0381     for (len = MAX_INSNS - 1; len > 0; --len)
0382         if (fp[len].code != 0 || fp[len].imm != 0)
0383             break;
0384     return len + 1;
0385 }
0386 
0387 static int load_sock_prog(const struct bpf_insn *prog,
0388               enum bpf_attach_type attach_type)
0389 {
0390     LIBBPF_OPTS(bpf_prog_load_opts, opts);
0391     int ret, insn_cnt;
0392 
0393     insn_cnt = probe_prog_length(prog);
0394 
0395     opts.expected_attach_type = attach_type;
0396     opts.log_buf = bpf_log_buf;
0397     opts.log_size = BPF_LOG_BUF_SIZE;
0398     opts.log_level = 2;
0399 
0400     ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", prog, insn_cnt, &opts);
0401     if (verbose && ret < 0)
0402         fprintf(stderr, "%s\n", bpf_log_buf);
0403 
0404     return ret;
0405 }
0406 
0407 static int attach_sock_prog(int cgfd, int progfd,
0408                 enum bpf_attach_type attach_type)
0409 {
0410     return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
0411 }
0412 
0413 static int bind_sock(int domain, int type, const char *ip,
0414              unsigned short port, unsigned short port_retry)
0415 {
0416     struct sockaddr_storage addr;
0417     struct sockaddr_in6 *addr6;
0418     struct sockaddr_in *addr4;
0419     int sockfd = -1;
0420     socklen_t len;
0421     int res = SUCCESS;
0422 
0423     sockfd = socket(domain, type, 0);
0424     if (sockfd < 0)
0425         goto err;
0426 
0427     memset(&addr, 0, sizeof(addr));
0428 
0429     if (domain == AF_INET) {
0430         len = sizeof(struct sockaddr_in);
0431         addr4 = (struct sockaddr_in *)&addr;
0432         addr4->sin_family = domain;
0433         addr4->sin_port = htons(port);
0434         if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
0435             goto err;
0436     } else if (domain == AF_INET6) {
0437         len = sizeof(struct sockaddr_in6);
0438         addr6 = (struct sockaddr_in6 *)&addr;
0439         addr6->sin6_family = domain;
0440         addr6->sin6_port = htons(port);
0441         if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
0442             goto err;
0443     } else {
0444         goto err;
0445     }
0446 
0447     if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
0448         /* sys_bind() may fail for different reasons, errno has to be
0449          * checked to confirm that BPF program rejected it.
0450          */
0451         if (errno != EPERM)
0452             goto err;
0453         if (port_retry)
0454             goto retry;
0455         res = BIND_REJECT;
0456         goto out;
0457     }
0458 
0459     goto out;
0460 retry:
0461     if (domain == AF_INET)
0462         addr4->sin_port = htons(port_retry);
0463     else
0464         addr6->sin6_port = htons(port_retry);
0465     if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {
0466         if (errno != EPERM)
0467             goto err;
0468         res = RETRY_REJECT;
0469     } else {
0470         res = RETRY_SUCCESS;
0471     }
0472     goto out;
0473 err:
0474     res = -1;
0475 out:
0476     close(sockfd);
0477     return res;
0478 }
0479 
0480 static int run_test_case(int cgfd, const struct sock_test *test)
0481 {
0482     int progfd = -1;
0483     int err = 0;
0484     int res;
0485 
0486     printf("Test case: %s .. ", test->descr);
0487     progfd = load_sock_prog(test->insns, test->expected_attach_type);
0488     if (progfd < 0) {
0489         if (test->result == LOAD_REJECT)
0490             goto out;
0491         else
0492             goto err;
0493     }
0494 
0495     if (attach_sock_prog(cgfd, progfd, test->attach_type) < 0) {
0496         if (test->result == ATTACH_REJECT)
0497             goto out;
0498         else
0499             goto err;
0500     }
0501 
0502     res = bind_sock(test->domain, test->type, test->ip, test->port,
0503             test->port_retry);
0504     if (res > 0 && test->result == res)
0505         goto out;
0506 
0507 err:
0508     err = -1;
0509 out:
0510     /* Detaching w/o checking return code: best effort attempt. */
0511     if (progfd != -1)
0512         bpf_prog_detach(cgfd, test->attach_type);
0513     close(progfd);
0514     printf("[%s]\n", err ? "FAIL" : "PASS");
0515     return err;
0516 }
0517 
0518 static int run_tests(int cgfd)
0519 {
0520     int passes = 0;
0521     int fails = 0;
0522     int i;
0523 
0524     for (i = 0; i < ARRAY_SIZE(tests); ++i) {
0525         if (run_test_case(cgfd, &tests[i]))
0526             ++fails;
0527         else
0528             ++passes;
0529     }
0530     printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
0531     return fails ? -1 : 0;
0532 }
0533 
0534 int main(int argc, char **argv)
0535 {
0536     int cgfd = -1;
0537     int err = 0;
0538 
0539     cgfd = cgroup_setup_and_join(CG_PATH);
0540     if (cgfd < 0)
0541         goto err;
0542 
0543     /* Use libbpf 1.0 API mode */
0544     libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
0545 
0546     if (run_tests(cgfd))
0547         goto err;
0548 
0549     goto out;
0550 err:
0551     err = -1;
0552 out:
0553     close(cgfd);
0554     cleanup_cgroup_environment();
0555     return err;
0556 }