0001
0002
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
0028 struct bpf_insn insns[MAX_INSNS];
0029 enum bpf_attach_type expected_attach_type;
0030 enum bpf_attach_type attach_type;
0031
0032 int domain;
0033 int type;
0034
0035 const char *ip;
0036 unsigned short port;
0037 unsigned short port_retry;
0038
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
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
0211 BPF_MOV64_IMM(BPF_REG_0, 0),
0212 BPF_JMP_A(1),
0213
0214
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
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
0241 BPF_MOV64_IMM(BPF_REG_0, 1),
0242 BPF_JMP_A(1),
0243
0244
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
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
0271 BPF_MOV64_IMM(BPF_REG_0, 0),
0272 BPF_JMP_A(1),
0273
0274
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
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
0302 BPF_MOV64_IMM(BPF_REG_0, 0),
0303 BPF_JMP_A(1),
0304
0305
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
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
0333 BPF_MOV64_IMM(BPF_REG_0, 0),
0334 BPF_JMP_A(1),
0335
0336
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
0449
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
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
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 }